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