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