• 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 
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