1 /* 2 * Copyright (C) 2020 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 android.net.cts; 18 19 import static android.content.pm.PackageManager.FEATURE_TELEPHONY; 20 import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; 21 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; 22 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; 23 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK; 24 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT; 25 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_SKIPPED; 26 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID; 27 import static android.net.ConnectivityDiagnosticsManager.DataStallReport; 28 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS; 29 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_TCP_METRICS; 30 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS; 31 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS; 32 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; 33 import static android.net.ConnectivityDiagnosticsManager.persistableBundleEquals; 34 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 35 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 36 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 37 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 38 import static android.net.NetworkCapabilities.TRANSPORT_TEST; 39 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback; 40 41 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; 42 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 43 44 import static org.junit.Assert.assertEquals; 45 import static org.junit.Assert.assertNotNull; 46 import static org.junit.Assert.assertNull; 47 import static org.junit.Assert.assertTrue; 48 import static org.junit.Assert.fail; 49 import static org.junit.Assume.assumeTrue; 50 51 import android.annotation.NonNull; 52 import android.content.BroadcastReceiver; 53 import android.content.Context; 54 import android.content.Intent; 55 import android.content.IntentFilter; 56 import android.content.pm.PackageInfo; 57 import android.content.pm.PackageManager; 58 import android.net.ConnectivityDiagnosticsManager; 59 import android.net.ConnectivityManager; 60 import android.net.LinkAddress; 61 import android.net.Network; 62 import android.net.NetworkCapabilities; 63 import android.net.NetworkRequest; 64 import android.net.TestNetworkInterface; 65 import android.net.TestNetworkManager; 66 import android.os.Binder; 67 import android.os.Build; 68 import android.os.IBinder; 69 import android.os.ParcelFileDescriptor; 70 import android.os.PersistableBundle; 71 import android.os.Process; 72 import android.platform.test.annotations.AppModeFull; 73 import android.telephony.CarrierConfigManager; 74 import android.telephony.SubscriptionManager; 75 import android.telephony.TelephonyManager; 76 import android.util.Pair; 77 78 import androidx.test.InstrumentationRegistry; 79 80 import com.android.internal.telephony.uicc.IccUtils; 81 import com.android.internal.util.ArrayUtils; 82 import com.android.modules.utils.build.SdkLevel; 83 import com.android.net.module.util.ArrayTrackRecord; 84 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 85 import com.android.testutils.DevSdkIgnoreRunner; 86 import com.android.testutils.SkipPresubmit; 87 88 import org.junit.After; 89 import org.junit.Before; 90 import org.junit.Test; 91 import org.junit.runner.RunWith; 92 93 import java.security.MessageDigest; 94 import java.util.ArrayList; 95 import java.util.List; 96 import java.util.concurrent.CountDownLatch; 97 import java.util.concurrent.Executor; 98 import java.util.concurrent.TimeUnit; 99 100 @RunWith(DevSdkIgnoreRunner.class) 101 @IgnoreUpTo(Build.VERSION_CODES.Q) // ConnectivityDiagnosticsManager did not exist in Q 102 @AppModeFull(reason = "CHANGE_NETWORK_STATE, MANAGE_TEST_NETWORKS not grantable to instant apps") 103 public class ConnectivityDiagnosticsManagerTest { 104 private static final int CALLBACK_TIMEOUT_MILLIS = 5000; 105 private static final int NO_CALLBACK_INVOKED_TIMEOUT = 500; 106 private static final long TIMESTAMP = 123456789L; 107 private static final int DNS_CONSECUTIVE_TIMEOUTS = 5; 108 private static final int COLLECTION_PERIOD_MILLIS = 5000; 109 private static final int FAIL_RATE_PERCENTAGE = 100; 110 private static final int UNKNOWN_DETECTION_METHOD = 4; 111 private static final int FILTERED_UNKNOWN_DETECTION_METHOD = 0; 112 private static final int CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT = 5000; 113 private static final int DELAY_FOR_ADMIN_UIDS_MILLIS = 2000; 114 115 private static final Executor INLINE_EXECUTOR = x -> x.run(); 116 117 private static final NetworkRequest TEST_NETWORK_REQUEST = 118 new NetworkRequest.Builder() 119 .addTransportType(TRANSPORT_TEST) 120 .removeCapability(NET_CAPABILITY_TRUSTED) 121 .removeCapability(NET_CAPABILITY_NOT_VPN) 122 .build(); 123 124 private static final String SHA_256 = "SHA-256"; 125 126 private static final NetworkRequest CELLULAR_NETWORK_REQUEST = 127 new NetworkRequest.Builder() 128 .addTransportType(TRANSPORT_CELLULAR) 129 .addCapability(NET_CAPABILITY_INTERNET) 130 .build(); 131 132 private static final IBinder BINDER = new Binder(); 133 134 // Lock for accessing Shell Permissions. Use of this lock around adoptShellPermissionIdentity, 135 // runWithShellPermissionIdentity, and callWithShellPermissionIdentity ensures Shell Permission 136 // is not interrupted by another operation (which would drop all previously adopted 137 // permissions). 138 private Object mShellPermissionsIdentityLock = new Object(); 139 140 private Context mContext; 141 private ConnectivityManager mConnectivityManager; 142 private ConnectivityDiagnosticsManager mCdm; 143 private CarrierConfigManager mCarrierConfigManager; 144 private PackageManager mPackageManager; 145 private TelephonyManager mTelephonyManager; 146 147 // Callback used to keep TestNetworks up when there are no other outstanding NetworkRequests 148 // for it. 149 private TestNetworkCallback mTestNetworkCallback; 150 private Network mTestNetwork; 151 private ParcelFileDescriptor mTestNetworkFD; 152 153 private List<TestConnectivityDiagnosticsCallback> mRegisteredCallbacks; 154 155 @Before setUp()156 public void setUp() throws Exception { 157 mContext = InstrumentationRegistry.getContext(); 158 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 159 mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class); 160 mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); 161 mPackageManager = mContext.getPackageManager(); 162 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 163 164 mTestNetworkCallback = new TestNetworkCallback(); 165 mConnectivityManager.requestNetwork(TEST_NETWORK_REQUEST, mTestNetworkCallback); 166 167 mRegisteredCallbacks = new ArrayList<>(); 168 } 169 170 @After tearDown()171 public void tearDown() throws Exception { 172 mConnectivityManager.unregisterNetworkCallback(mTestNetworkCallback); 173 if (mTestNetwork != null) { 174 runWithShellPermissionIdentity(() -> { 175 final TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); 176 tnm.teardownTestNetwork(mTestNetwork); 177 }); 178 mTestNetwork = null; 179 } 180 181 if (mTestNetworkFD != null) { 182 mTestNetworkFD.close(); 183 mTestNetworkFD = null; 184 } 185 186 for (TestConnectivityDiagnosticsCallback cb : mRegisteredCallbacks) { 187 mCdm.unregisterConnectivityDiagnosticsCallback(cb); 188 } 189 } 190 191 @Test testRegisterConnectivityDiagnosticsCallback()192 public void testRegisterConnectivityDiagnosticsCallback() throws Exception { 193 mTestNetworkFD = setUpTestNetwork().getFileDescriptor(); 194 mTestNetwork = mTestNetworkCallback.waitForAvailable(); 195 196 final TestConnectivityDiagnosticsCallback cb = 197 createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST); 198 199 final String interfaceName = 200 mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName(); 201 202 cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName); 203 cb.assertNoCallback(); 204 } 205 206 @SkipPresubmit(reason = "Flaky: b/159718782; add to presubmit after fixing") 207 @Test testRegisterCallbackWithCarrierPrivileges()208 public void testRegisterCallbackWithCarrierPrivileges() throws Exception { 209 assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)); 210 211 final int subId = SubscriptionManager.getDefaultSubscriptionId(); 212 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 213 fail("Need an active subscription. Please ensure that the device has working mobile" 214 + " data."); 215 } 216 217 final CarrierConfigReceiver carrierConfigReceiver = new CarrierConfigReceiver(subId); 218 mContext.registerReceiver( 219 carrierConfigReceiver, 220 new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 221 222 final TestNetworkCallback testNetworkCallback = new TestNetworkCallback(); 223 224 try { 225 doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable( 226 subId, carrierConfigReceiver, testNetworkCallback); 227 } finally { 228 runWithShellPermissionIdentity( 229 () -> mCarrierConfigManager.overrideConfig(subId, null), 230 android.Manifest.permission.MODIFY_PHONE_STATE); 231 mConnectivityManager.unregisterNetworkCallback(testNetworkCallback); 232 mContext.unregisterReceiver(carrierConfigReceiver); 233 } 234 } 235 getCertHashForThisPackage()236 private String getCertHashForThisPackage() throws Exception { 237 final PackageInfo pkgInfo = 238 mPackageManager.getPackageInfo( 239 mContext.getOpPackageName(), PackageManager.GET_SIGNATURES); 240 final MessageDigest md = MessageDigest.getInstance(SHA_256); 241 final byte[] certHash = md.digest(pkgInfo.signatures[0].toByteArray()); 242 return IccUtils.bytesToHexString(certHash); 243 } 244 doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable( int subId, @NonNull CarrierConfigReceiver carrierConfigReceiver, @NonNull TestNetworkCallback testNetworkCallback)245 private void doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable( 246 int subId, 247 @NonNull CarrierConfigReceiver carrierConfigReceiver, 248 @NonNull TestNetworkCallback testNetworkCallback) 249 throws Exception { 250 final PersistableBundle carrierConfigs = new PersistableBundle(); 251 carrierConfigs.putStringArray( 252 CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, 253 new String[] {getCertHashForThisPackage()}); 254 255 synchronized (mShellPermissionsIdentityLock) { 256 runWithShellPermissionIdentity( 257 () -> { 258 mCarrierConfigManager.overrideConfig(subId, carrierConfigs); 259 mCarrierConfigManager.notifyConfigChangedForSubId(subId); 260 }, 261 android.Manifest.permission.MODIFY_PHONE_STATE); 262 } 263 264 // TODO(b/157779832): This should use android.permission.CHANGE_NETWORK_STATE. However, the 265 // shell does not have CHANGE_NETWORK_STATE, so use CONNECTIVITY_INTERNAL until the shell 266 // permissions are updated. 267 synchronized (mShellPermissionsIdentityLock) { 268 runWithShellPermissionIdentity( 269 () -> mConnectivityManager.requestNetwork( 270 CELLULAR_NETWORK_REQUEST, testNetworkCallback), 271 android.Manifest.permission.CONNECTIVITY_INTERNAL); 272 } 273 274 final Network network = testNetworkCallback.waitForAvailable(); 275 assertNotNull(network); 276 277 assertTrue("Didn't receive broadcast for ACTION_CARRIER_CONFIG_CHANGED for subId=" + subId, 278 carrierConfigReceiver.waitForCarrierConfigChanged()); 279 assertTrue("Don't have Carrier Privileges after adding cert for this package", 280 mTelephonyManager.createForSubscriptionId(subId).hasCarrierPrivileges()); 281 282 // Wait for CarrierPrivilegesTracker to receive the ACTION_CARRIER_CONFIG_CHANGED 283 // broadcast. CPT then needs to update the corresponding DataConnection, which then 284 // updates ConnectivityService. Unfortunately, this update to the NetworkCapabilities in 285 // CS does not trigger NetworkCallback#onCapabilitiesChanged as changing the 286 // administratorUids is not a publicly visible change. In lieu of a better signal to 287 // detministically wait for, use Thread#sleep here. 288 // TODO(b/157949581): replace this Thread#sleep with a deterministic signal 289 Thread.sleep(DELAY_FOR_ADMIN_UIDS_MILLIS); 290 291 final TestConnectivityDiagnosticsCallback connDiagsCallback = 292 createAndRegisterConnectivityDiagnosticsCallback(CELLULAR_NETWORK_REQUEST); 293 294 final String interfaceName = 295 mConnectivityManager.getLinkProperties(network).getInterfaceName(); 296 connDiagsCallback.expectOnConnectivityReportAvailable( 297 network, interfaceName, TRANSPORT_CELLULAR); 298 connDiagsCallback.assertNoCallback(); 299 } 300 301 @Test testRegisterDuplicateConnectivityDiagnosticsCallback()302 public void testRegisterDuplicateConnectivityDiagnosticsCallback() { 303 final TestConnectivityDiagnosticsCallback cb = 304 createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST); 305 306 try { 307 mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb); 308 fail("Registering the same callback twice should throw an IllegalArgumentException"); 309 } catch (IllegalArgumentException expected) { 310 } 311 } 312 313 @Test testUnregisterConnectivityDiagnosticsCallback()314 public void testUnregisterConnectivityDiagnosticsCallback() { 315 final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback(); 316 mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb); 317 mCdm.unregisterConnectivityDiagnosticsCallback(cb); 318 } 319 320 @Test testUnregisterUnknownConnectivityDiagnosticsCallback()321 public void testUnregisterUnknownConnectivityDiagnosticsCallback() { 322 // Expected to silently ignore the unregister() call 323 mCdm.unregisterConnectivityDiagnosticsCallback(new TestConnectivityDiagnosticsCallback()); 324 } 325 326 @Test testOnConnectivityReportAvailable()327 public void testOnConnectivityReportAvailable() throws Exception { 328 final TestConnectivityDiagnosticsCallback cb = 329 createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST); 330 331 mTestNetworkFD = setUpTestNetwork().getFileDescriptor(); 332 mTestNetwork = mTestNetworkCallback.waitForAvailable(); 333 334 final String interfaceName = 335 mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName(); 336 337 cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName); 338 cb.assertNoCallback(); 339 } 340 341 @Test testOnDataStallSuspected_DnsEvents()342 public void testOnDataStallSuspected_DnsEvents() throws Exception { 343 final PersistableBundle extras = new PersistableBundle(); 344 extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, DNS_CONSECUTIVE_TIMEOUTS); 345 346 verifyOnDataStallSuspected(DETECTION_METHOD_DNS_EVENTS, TIMESTAMP, extras); 347 } 348 349 @Test testOnDataStallSuspected_TcpMetrics()350 public void testOnDataStallSuspected_TcpMetrics() throws Exception { 351 final PersistableBundle extras = new PersistableBundle(); 352 extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS, COLLECTION_PERIOD_MILLIS); 353 extras.putInt(KEY_TCP_PACKET_FAIL_RATE, FAIL_RATE_PERCENTAGE); 354 355 verifyOnDataStallSuspected(DETECTION_METHOD_TCP_METRICS, TIMESTAMP, extras); 356 } 357 358 @Test testOnDataStallSuspected_UnknownDetectionMethod()359 public void testOnDataStallSuspected_UnknownDetectionMethod() throws Exception { 360 verifyOnDataStallSuspected( 361 UNKNOWN_DETECTION_METHOD, 362 FILTERED_UNKNOWN_DETECTION_METHOD, 363 TIMESTAMP, 364 PersistableBundle.EMPTY); 365 } 366 verifyOnDataStallSuspected( int detectionMethod, long timestampMillis, @NonNull PersistableBundle extras)367 private void verifyOnDataStallSuspected( 368 int detectionMethod, long timestampMillis, @NonNull PersistableBundle extras) 369 throws Exception { 370 // Input detection method is expected to match received detection method 371 verifyOnDataStallSuspected(detectionMethod, detectionMethod, timestampMillis, extras); 372 } 373 verifyOnDataStallSuspected( int inputDetectionMethod, int expectedDetectionMethod, long timestampMillis, @NonNull PersistableBundle extras)374 private void verifyOnDataStallSuspected( 375 int inputDetectionMethod, 376 int expectedDetectionMethod, 377 long timestampMillis, 378 @NonNull PersistableBundle extras) 379 throws Exception { 380 mTestNetworkFD = setUpTestNetwork().getFileDescriptor(); 381 mTestNetwork = mTestNetworkCallback.waitForAvailable(); 382 383 final TestConnectivityDiagnosticsCallback cb = 384 createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST); 385 386 final String interfaceName = 387 mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName(); 388 389 cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName); 390 391 runWithShellPermissionIdentity( 392 () -> mConnectivityManager.simulateDataStall( 393 inputDetectionMethod, timestampMillis, mTestNetwork, extras), 394 android.Manifest.permission.MANAGE_TEST_NETWORKS); 395 396 cb.expectOnDataStallSuspected( 397 mTestNetwork, interfaceName, expectedDetectionMethod, timestampMillis, extras); 398 cb.assertNoCallback(); 399 } 400 401 @Test testOnNetworkConnectivityReportedTrue()402 public void testOnNetworkConnectivityReportedTrue() throws Exception { 403 verifyOnNetworkConnectivityReported(true /* hasConnectivity */); 404 } 405 406 @Test testOnNetworkConnectivityReportedFalse()407 public void testOnNetworkConnectivityReportedFalse() throws Exception { 408 verifyOnNetworkConnectivityReported(false /* hasConnectivity */); 409 } 410 verifyOnNetworkConnectivityReported(boolean hasConnectivity)411 private void verifyOnNetworkConnectivityReported(boolean hasConnectivity) throws Exception { 412 mTestNetworkFD = setUpTestNetwork().getFileDescriptor(); 413 mTestNetwork = mTestNetworkCallback.waitForAvailable(); 414 415 final TestConnectivityDiagnosticsCallback cb = 416 createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST); 417 418 // onConnectivityReportAvailable always invoked when the test network is established 419 final String interfaceName = 420 mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName(); 421 cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName); 422 cb.assertNoCallback(); 423 424 mConnectivityManager.reportNetworkConnectivity(mTestNetwork, hasConnectivity); 425 426 cb.expectOnNetworkConnectivityReported(mTestNetwork, hasConnectivity); 427 428 // if hasConnectivity does not match the network's known connectivity, it will be 429 // revalidated which will trigger another onConnectivityReportAvailable callback. 430 if (!hasConnectivity) { 431 cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName); 432 } else if (SdkLevel.isAtLeastS()) { 433 // All calls to #onNetworkConnectivityReported are expected to be accompanied by a call 434 // to #onConnectivityReportAvailable after a mainline update in the S timeframe. 435 // Optionally validate this, but do not fail if it does not exist. 436 cb.maybeVerifyOnConnectivityReportAvailable(mTestNetwork, interfaceName, TRANSPORT_TEST, 437 false /* requireCallbackFired */); 438 } 439 440 cb.assertNoCallback(); 441 } 442 createAndRegisterConnectivityDiagnosticsCallback( NetworkRequest request)443 private TestConnectivityDiagnosticsCallback createAndRegisterConnectivityDiagnosticsCallback( 444 NetworkRequest request) { 445 final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback(); 446 mCdm.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, cb); 447 mRegisteredCallbacks.add(cb); 448 return cb; 449 } 450 451 /** 452 * Registers a test NetworkAgent with ConnectivityService with limited capabilities, which leads 453 * to the Network being validated. 454 */ 455 @NonNull setUpTestNetwork()456 private TestNetworkInterface setUpTestNetwork() throws Exception { 457 final int[] administratorUids = new int[] {Process.myUid()}; 458 return callWithShellPermissionIdentity( 459 () -> { 460 final TestNetworkManager tnm = 461 mContext.getSystemService(TestNetworkManager.class); 462 final TestNetworkInterface tni = tnm.createTunInterface(new LinkAddress[0]); 463 tnm.setupTestNetwork(tni.getInterfaceName(), administratorUids, BINDER); 464 return tni; 465 }); 466 } 467 468 private static class TestConnectivityDiagnosticsCallback 469 extends ConnectivityDiagnosticsCallback { 470 private final ArrayTrackRecord<Object>.ReadHead mHistory = 471 new ArrayTrackRecord<Object>().newReadHead(); 472 473 @Override onConnectivityReportAvailable(ConnectivityReport report)474 public void onConnectivityReportAvailable(ConnectivityReport report) { 475 mHistory.add(report); 476 } 477 478 @Override onDataStallSuspected(DataStallReport report)479 public void onDataStallSuspected(DataStallReport report) { 480 mHistory.add(report); 481 } 482 483 @Override onNetworkConnectivityReported(Network network, boolean hasConnectivity)484 public void onNetworkConnectivityReported(Network network, boolean hasConnectivity) { 485 mHistory.add(new Pair<Network, Boolean>(network, hasConnectivity)); 486 } 487 expectOnConnectivityReportAvailable( @onNull Network network, @NonNull String interfaceName)488 public void expectOnConnectivityReportAvailable( 489 @NonNull Network network, @NonNull String interfaceName) { 490 expectOnConnectivityReportAvailable( 491 network, interfaceName, TRANSPORT_TEST); 492 } 493 expectOnConnectivityReportAvailable(@onNull Network network, @NonNull String interfaceName, int transportType)494 public void expectOnConnectivityReportAvailable(@NonNull Network network, 495 @NonNull String interfaceName, int transportType) { 496 maybeVerifyOnConnectivityReportAvailable(network, interfaceName, transportType, 497 true /* requireCallbackFired */); 498 } 499 maybeVerifyOnConnectivityReportAvailable(@onNull Network network, @NonNull String interfaceName, int transportType, boolean requireCallbackFired)500 public void maybeVerifyOnConnectivityReportAvailable(@NonNull Network network, 501 @NonNull String interfaceName, int transportType, boolean requireCallbackFired) { 502 final ConnectivityReport result = 503 (ConnectivityReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true); 504 505 // If callback is not required and there is no report, exit early. 506 if (!requireCallbackFired && result == null) { 507 return; 508 } 509 assertEquals(network, result.getNetwork()); 510 511 final NetworkCapabilities nc = result.getNetworkCapabilities(); 512 assertNotNull(nc); 513 assertTrue(nc.hasTransport(transportType)); 514 assertNotNull(result.getLinkProperties()); 515 assertEquals(interfaceName, result.getLinkProperties().getInterfaceName()); 516 517 final PersistableBundle extras = result.getAdditionalInfo(); 518 assertTrue(extras.containsKey(KEY_NETWORK_VALIDATION_RESULT)); 519 final int actualValidationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT); 520 521 // Allow RESULT_VALID for networks that are expected to be skipped. Android S shipped 522 // with validation results being reported as VALID, but the behavior will be updated via 523 // mainline update. Allow both behaviors, and let MTS enforce stricter behavior 524 if (actualValidationResult != NETWORK_VALIDATION_RESULT_SKIPPED 525 && actualValidationResult != NETWORK_VALIDATION_RESULT_VALID) { 526 fail("Network validation result was incorrect; expected skipped or valid, but " 527 + "got " + actualValidationResult); 528 } 529 530 assertTrue(extras.containsKey(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK)); 531 final int probesSucceeded = extras.getInt(KEY_NETWORK_VALIDATION_RESULT); 532 assertTrue("PROBES_SUCCEEDED mask not in expected range", probesSucceeded >= 0); 533 534 assertTrue(extras.containsKey(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK)); 535 final int probesAttempted = extras.getInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK); 536 assertTrue("PROBES_ATTEMPTED mask not in expected range", probesAttempted >= 0); 537 } 538 expectOnDataStallSuspected( @onNull Network network, @NonNull String interfaceName, int detectionMethod, long timestampMillis, @NonNull PersistableBundle extras)539 public void expectOnDataStallSuspected( 540 @NonNull Network network, 541 @NonNull String interfaceName, 542 int detectionMethod, 543 long timestampMillis, 544 @NonNull PersistableBundle extras) { 545 final DataStallReport result = 546 (DataStallReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true); 547 assertEquals(network, result.getNetwork()); 548 assertEquals(detectionMethod, result.getDetectionMethod()); 549 assertEquals(timestampMillis, result.getReportTimestamp()); 550 551 final NetworkCapabilities nc = result.getNetworkCapabilities(); 552 assertNotNull(nc); 553 assertTrue(nc.hasTransport(TRANSPORT_TEST)); 554 assertNotNull(result.getLinkProperties()); 555 assertEquals(interfaceName, result.getLinkProperties().getInterfaceName()); 556 557 assertTrue(persistableBundleEquals(extras, result.getStallDetails())); 558 } 559 expectOnNetworkConnectivityReported( @onNull Network network, boolean hasConnectivity)560 public void expectOnNetworkConnectivityReported( 561 @NonNull Network network, boolean hasConnectivity) { 562 final Pair<Network, Boolean> result = 563 (Pair<Network, Boolean>) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true); 564 assertEquals(network, result.first /* network */); 565 assertEquals(hasConnectivity, result.second /* hasConnectivity */); 566 } 567 assertNoCallback()568 public void assertNoCallback() { 569 // If no more callbacks exist, there should be nothing left in the ReadHead 570 assertNull("Unexpected event in history", 571 mHistory.poll(NO_CALLBACK_INVOKED_TIMEOUT, x -> true)); 572 } 573 } 574 575 private class CarrierConfigReceiver extends BroadcastReceiver { 576 // CountDownLatch used to wait for this BroadcastReceiver to be notified of a CarrierConfig 577 // change. This latch will be counted down if a broadcast indicates this package has carrier 578 // configs, or if an Exception occurs in #onReceive. 579 private final CountDownLatch mLatch = new CountDownLatch(1); 580 private final int mSubId; 581 582 // #onReceive may encounter Exceptions while running on the Process' main Thread and 583 // #waitForCarrierConfigChanged checks the cached Exception from the test Thread. These 584 // Exceptions must be cached and thrown later, as throwing on the Process' main Thread will 585 // crash the process and cause other tests to fail. 586 private Exception mOnReceiveException; 587 CarrierConfigReceiver(int subId)588 CarrierConfigReceiver(int subId) { 589 mSubId = subId; 590 } 591 592 @Override onReceive(Context context, Intent intent)593 public void onReceive(Context context, Intent intent) { 594 if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { 595 // Received an incorrect broadcast - ignore 596 return; 597 } 598 599 final int subId = 600 intent.getIntExtra( 601 CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 602 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 603 if (mSubId != subId) { 604 // Received a broadcast for the wrong subId - ignore 605 return; 606 } 607 608 final PersistableBundle carrierConfigs; 609 try { 610 synchronized (mShellPermissionsIdentityLock) { 611 carrierConfigs = callWithShellPermissionIdentity( 612 () -> mCarrierConfigManager.getConfigForSubId(subId), 613 android.Manifest.permission.READ_PHONE_STATE); 614 } 615 } catch (Exception exception) { 616 // callWithShellPermissionIdentity() threw an Exception - cache it and allow 617 // waitForCarrierConfigChanged() to throw it 618 mOnReceiveException = exception; 619 mLatch.countDown(); 620 return; 621 } 622 623 if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) { 624 // Configs are not for an identified carrier (meaning they are defaults) - ignore 625 return; 626 } 627 628 final String[] certs = carrierConfigs.getStringArray( 629 CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY); 630 try { 631 if (ArrayUtils.contains(certs, getCertHashForThisPackage())) { 632 // Received an update for this package's cert hash - countdown and exit 633 mLatch.countDown(); 634 } 635 // Broadcast is for the right subId, but does not show this package as Carrier 636 // Privileged. Keep waiting for a broadcast that indicates Carrier Privileges. 637 } catch (Exception exception) { 638 // getCertHashForThisPackage() threw an Exception - cache it and allow 639 // waitForCarrierConfigChanged() to throw it 640 mOnReceiveException = exception; 641 mLatch.countDown(); 642 } 643 } 644 645 /** 646 * Waits for the CarrierConfig changed broadcast to reach this CarrierConfigReceiver. 647 * 648 * <p>Must be called from the Test Thread. 649 * 650 * @throws Exception if an Exception occurred during any #onReceive invocation 651 */ waitForCarrierConfigChanged()652 boolean waitForCarrierConfigChanged() throws Exception { 653 final boolean result = mLatch.await(CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT, 654 TimeUnit.MILLISECONDS); 655 656 if (mOnReceiveException != null) { 657 throw mOnReceiveException; 658 } 659 660 return result; 661 } 662 } 663 } 664