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