1 /*
2  * Copyright (C) 2019 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;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.StringDef;
23 import android.content.Context;
24 import android.os.Binder;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.PersistableBundle;
28 import android.os.RemoteException;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.util.Preconditions;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.Executor;
39 
40 /**
41  * Class that provides utilities for collecting network connectivity diagnostics information.
42  * Connectivity information is made available through triggerable diagnostics tools and by listening
43  * to System validations. Some diagnostics information may be permissions-restricted.
44  *
45  * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network
46  * connectivity on a user device. These tools will provide several mechanisms for these applications
47  * to be alerted to network conditions as well as diagnose potential network issues themselves.
48  *
49  * <p>The primary responsibilities of this class are to:
50  *
51  * <ul>
52  *   <li>Allow permissioned applications to register and unregister callbacks for network event
53  *       notifications
54  *   <li>Invoke callbacks for network event notifications, including:
55  *       <ul>
56  *         <li>Network validations
57  *         <li>Data stalls
58  *         <li>Connectivity reports from applications
59  *       </ul>
60  * </ul>
61  */
62 public class ConnectivityDiagnosticsManager {
63     /** @hide */
64     @VisibleForTesting
65     public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
66             sCallbacks = new ConcurrentHashMap<>();
67 
68     private final Context mContext;
69     private final IConnectivityManager mService;
70 
71     /** @hide */
ConnectivityDiagnosticsManager(Context context, IConnectivityManager service)72     public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
73         mContext = Preconditions.checkNotNull(context, "missing context");
74         mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
75     }
76 
77     /** @hide */
78     @VisibleForTesting
persistableBundleEquals( @ullable PersistableBundle a, @Nullable PersistableBundle b)79     public static boolean persistableBundleEquals(
80             @Nullable PersistableBundle a, @Nullable PersistableBundle b) {
81         if (a == b) return true;
82         if (a == null || b == null) return false;
83         if (!Objects.equals(a.keySet(), b.keySet())) return false;
84         for (String key : a.keySet()) {
85             if (!Objects.equals(a.get(key), b.get(key))) return false;
86         }
87         return true;
88     }
89 
90     /** Class that includes connectivity information for a specific Network at a specific time. */
91     public static final class ConnectivityReport implements Parcelable {
92         /**
93          * The overall status of the network is that it is invalid; it neither provides
94          * connectivity nor has been exempted from validation.
95          */
96         public static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
97 
98         /**
99          * The overall status of the network is that it is valid, this may be because it provides
100          * full Internet access (all probes succeeded), or because other properties of the network
101          * caused probes not to be run.
102          */
103         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID
104         public static final int NETWORK_VALIDATION_RESULT_VALID = 1;
105 
106         /**
107          * The overall status of the network is that it provides partial connectivity; some
108          * probed services succeeded but others failed.
109          */
110         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
111         public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2;
112 
113         /**
114          * Due to the properties of the network, validation was not performed.
115          */
116         public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3;
117 
118         /** @hide */
119         @IntDef(
120                 prefix = {"NETWORK_VALIDATION_RESULT_"},
121                 value = {
122                         NETWORK_VALIDATION_RESULT_INVALID,
123                         NETWORK_VALIDATION_RESULT_VALID,
124                         NETWORK_VALIDATION_RESULT_PARTIALLY_VALID,
125                         NETWORK_VALIDATION_RESULT_SKIPPED
126                 })
127         @Retention(RetentionPolicy.SOURCE)
128         public @interface NetworkValidationResult {}
129 
130         /**
131          * The overall validation result for the Network being reported on.
132          *
133          * <p>The possible values for this key are:
134          * {@link #NETWORK_VALIDATION_RESULT_INVALID},
135          * {@link #NETWORK_VALIDATION_RESULT_VALID},
136          * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
137          * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
138          *
139          * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
140          */
141         @NetworkValidationResult
142         public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
143 
144         /** DNS probe. */
145         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS
146         public static final int NETWORK_PROBE_DNS = 0x04;
147 
148         /** HTTP probe. */
149         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP
150         public static final int NETWORK_PROBE_HTTP = 0x08;
151 
152         /** HTTPS probe. */
153         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
154         public static final int NETWORK_PROBE_HTTPS = 0x10;
155 
156         /** Captive portal fallback probe. */
157         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK
158         public static final int NETWORK_PROBE_FALLBACK = 0x20;
159 
160         /** Private DNS (DNS over TLS) probd. */
161         // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS
162         public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40;
163 
164         /** @hide */
165         @IntDef(
166                 prefix = {"NETWORK_PROBE_"},
167                 value = {
168                         NETWORK_PROBE_DNS,
169                         NETWORK_PROBE_HTTP,
170                         NETWORK_PROBE_HTTPS,
171                         NETWORK_PROBE_FALLBACK,
172                         NETWORK_PROBE_PRIVATE_DNS
173                 })
174         @Retention(RetentionPolicy.SOURCE)
175         public @interface NetworkProbe {}
176 
177         /**
178          * A bitmask of network validation probes that succeeded.
179          *
180          * <p>The possible bits values reported by this key are:
181          * {@link #NETWORK_PROBE_DNS},
182          * {@link #NETWORK_PROBE_HTTP},
183          * {@link #NETWORK_PROBE_HTTPS},
184          * {@link #NETWORK_PROBE_FALLBACK},
185          * {@link #NETWORK_PROBE_PRIVATE_DNS}.
186          */
187         @NetworkProbe
188         public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK =
189                 "networkProbesSucceeded";
190 
191         /**
192          * A bitmask of network validation probes that were attempted.
193          *
194          * <p>These probes may have failed or may be incomplete at the time of this report.
195          *
196          * <p>The possible bits values reported by this key are:
197          * {@link #NETWORK_PROBE_DNS},
198          * {@link #NETWORK_PROBE_HTTP},
199          * {@link #NETWORK_PROBE_HTTPS},
200          * {@link #NETWORK_PROBE_FALLBACK},
201          * {@link #NETWORK_PROBE_PRIVATE_DNS}.
202          */
203         @NetworkProbe
204         public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
205                 "networkProbesAttempted";
206 
207         /** @hide */
208         @StringDef(prefix = {"KEY_"}, value = {
209                 KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK,
210                 KEY_NETWORK_PROBES_ATTEMPTED_BITMASK})
211         @Retention(RetentionPolicy.SOURCE)
212         public @interface ConnectivityReportBundleKeys {}
213 
214         /** The Network for which this ConnectivityReport applied */
215         @NonNull private final Network mNetwork;
216 
217         /**
218          * The timestamp for the report. The timestamp is taken from {@link
219          * System#currentTimeMillis}.
220          */
221         private final long mReportTimestamp;
222 
223         /** LinkProperties available on the Network at the reported timestamp */
224         @NonNull private final LinkProperties mLinkProperties;
225 
226         /** NetworkCapabilities available on the Network at the reported timestamp */
227         @NonNull private final NetworkCapabilities mNetworkCapabilities;
228 
229         /** PersistableBundle that may contain additional info about the report */
230         @NonNull private final PersistableBundle mAdditionalInfo;
231 
232         /**
233          * Constructor for ConnectivityReport.
234          *
235          * <p>Apps should obtain instances through {@link
236          * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
237          * their own instances (unless for testing purposes).
238          *
239          * @param network The Network for which this ConnectivityReport applies
240          * @param reportTimestamp The timestamp for the report
241          * @param linkProperties The LinkProperties available on network at reportTimestamp
242          * @param networkCapabilities The NetworkCapabilities available on network at
243          *     reportTimestamp
244          * @param additionalInfo A PersistableBundle that may contain additional info about the
245          *     report
246          */
ConnectivityReport( @onNull Network network, long reportTimestamp, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull PersistableBundle additionalInfo)247         public ConnectivityReport(
248                 @NonNull Network network,
249                 long reportTimestamp,
250                 @NonNull LinkProperties linkProperties,
251                 @NonNull NetworkCapabilities networkCapabilities,
252                 @NonNull PersistableBundle additionalInfo) {
253             mNetwork = network;
254             mReportTimestamp = reportTimestamp;
255             mLinkProperties = new LinkProperties(linkProperties);
256             mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
257             mAdditionalInfo = additionalInfo;
258         }
259 
260         /**
261          * Returns the Network for this ConnectivityReport.
262          *
263          * @return The Network for which this ConnectivityReport applied
264          */
265         @NonNull
getNetwork()266         public Network getNetwork() {
267             return mNetwork;
268         }
269 
270         /**
271          * Returns the epoch timestamp (milliseconds) for when this report was taken.
272          *
273          * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
274          */
getReportTimestamp()275         public long getReportTimestamp() {
276             return mReportTimestamp;
277         }
278 
279         /**
280          * Returns the LinkProperties available when this report was taken.
281          *
282          * @return LinkProperties available on the Network at the reported timestamp
283          */
284         @NonNull
getLinkProperties()285         public LinkProperties getLinkProperties() {
286             return new LinkProperties(mLinkProperties);
287         }
288 
289         /**
290          * Returns the NetworkCapabilities when this report was taken.
291          *
292          * @return NetworkCapabilities available on the Network at the reported timestamp
293          */
294         @NonNull
getNetworkCapabilities()295         public NetworkCapabilities getNetworkCapabilities() {
296             return new NetworkCapabilities(mNetworkCapabilities);
297         }
298 
299         /**
300          * Returns a PersistableBundle with additional info for this report.
301          *
302          * @return PersistableBundle that may contain additional info about the report
303          */
304         @NonNull
getAdditionalInfo()305         public PersistableBundle getAdditionalInfo() {
306             return new PersistableBundle(mAdditionalInfo);
307         }
308 
309         @Override
equals(@ullable Object o)310         public boolean equals(@Nullable Object o) {
311             if (this == o) return true;
312             if (!(o instanceof ConnectivityReport)) return false;
313             final ConnectivityReport that = (ConnectivityReport) o;
314 
315             // PersistableBundle is optimized to avoid unparcelling data unless fields are
316             // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
317             // {@link PersistableBundle#kindofEquals}.
318             return mReportTimestamp == that.mReportTimestamp
319                     && mNetwork.equals(that.mNetwork)
320                     && mLinkProperties.equals(that.mLinkProperties)
321                     && mNetworkCapabilities.equals(that.mNetworkCapabilities)
322                     && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo);
323         }
324 
325         @Override
hashCode()326         public int hashCode() {
327             return Objects.hash(
328                     mNetwork,
329                     mReportTimestamp,
330                     mLinkProperties,
331                     mNetworkCapabilities,
332                     mAdditionalInfo);
333         }
334 
335         /** {@inheritDoc} */
336         @Override
describeContents()337         public int describeContents() {
338             return 0;
339         }
340 
341         /** {@inheritDoc} */
342         @Override
writeToParcel(@onNull Parcel dest, int flags)343         public void writeToParcel(@NonNull Parcel dest, int flags) {
344             dest.writeParcelable(mNetwork, flags);
345             dest.writeLong(mReportTimestamp);
346             dest.writeParcelable(mLinkProperties, flags);
347             dest.writeParcelable(mNetworkCapabilities, flags);
348             dest.writeParcelable(mAdditionalInfo, flags);
349         }
350 
351         /** Implement the Parcelable interface */
352         public static final @NonNull Creator<ConnectivityReport> CREATOR =
353                 new Creator<ConnectivityReport>() {
354                     public ConnectivityReport createFromParcel(Parcel in) {
355                         return new ConnectivityReport(
356                                 in.readParcelable(null),
357                                 in.readLong(),
358                                 in.readParcelable(null),
359                                 in.readParcelable(null),
360                                 in.readParcelable(null));
361                     }
362 
363                     public ConnectivityReport[] newArray(int size) {
364                         return new ConnectivityReport[size];
365                     }
366                 };
367     }
368 
369     /** Class that includes information for a suspected data stall on a specific Network */
370     public static final class DataStallReport implements Parcelable {
371         /**
372          * Indicates that the Data Stall was detected using DNS events.
373          */
374         public static final int DETECTION_METHOD_DNS_EVENTS = 1;
375 
376         /**
377          * Indicates that the Data Stall was detected using TCP metrics.
378          */
379         public static final int DETECTION_METHOD_TCP_METRICS = 2;
380 
381         /** @hide */
382         @Retention(RetentionPolicy.SOURCE)
383         @IntDef(
384                 prefix = {"DETECTION_METHOD_"},
385                 value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
386         public @interface DetectionMethod {}
387 
388         /**
389          * This key represents the period in milliseconds over which other included TCP metrics
390          * were measured.
391          *
392          * <p>This key will be included if the data stall detection method is
393          * {@link #DETECTION_METHOD_TCP_METRICS}.
394          *
395          * <p>This value is an int.
396          */
397         public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS =
398                 "tcpMetricsCollectionPeriodMillis";
399 
400         /**
401          * This key represents the fail rate of TCP packets when the suspected data stall was
402          * detected.
403          *
404          * <p>This key will be included if the data stall detection method is
405          * {@link #DETECTION_METHOD_TCP_METRICS}.
406          *
407          * <p>This value is an int percentage between 0 and 100.
408          */
409         public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
410 
411         /**
412          * This key represents the consecutive number of DNS timeouts that have occurred.
413          *
414          * <p>The consecutive count will be reset any time a DNS response is received.
415          *
416          * <p>This key will be included if the data stall detection method is
417          * {@link #DETECTION_METHOD_DNS_EVENTS}.
418          *
419          * <p>This value is an int.
420          */
421         public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
422 
423         /** @hide */
424         @Retention(RetentionPolicy.SOURCE)
425         @StringDef(prefix = {"KEY_"}, value = {
426                 KEY_TCP_PACKET_FAIL_RATE,
427                 KEY_DNS_CONSECUTIVE_TIMEOUTS
428         })
429         public @interface DataStallReportBundleKeys {}
430 
431         /** The Network for which this DataStallReport applied */
432         @NonNull private final Network mNetwork;
433 
434         /**
435          * The timestamp for the report. The timestamp is taken from {@link
436          * System#currentTimeMillis}.
437          */
438         private long mReportTimestamp;
439 
440         /** A bitmask of the detection methods used to identify the suspected data stall */
441         @DetectionMethod private final int mDetectionMethod;
442 
443         /** LinkProperties available on the Network at the reported timestamp */
444         @NonNull private final LinkProperties mLinkProperties;
445 
446         /** NetworkCapabilities available on the Network at the reported timestamp */
447         @NonNull private final NetworkCapabilities mNetworkCapabilities;
448 
449         /** PersistableBundle that may contain additional information on the suspected data stall */
450         @NonNull private final PersistableBundle mStallDetails;
451 
452         /**
453          * Constructor for DataStallReport.
454          *
455          * <p>Apps should obtain instances through {@link
456          * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own
457          * instances (unless for testing purposes).
458          *
459          * @param network The Network for which this DataStallReport applies
460          * @param reportTimestamp The timestamp for the report
461          * @param detectionMethod The detection method used to identify this data stall
462          * @param linkProperties The LinkProperties available on network at reportTimestamp
463          * @param networkCapabilities The NetworkCapabilities available on network at
464          *     reportTimestamp
465          * @param stallDetails A PersistableBundle that may contain additional info about the report
466          */
DataStallReport( @onNull Network network, long reportTimestamp, @DetectionMethod int detectionMethod, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull PersistableBundle stallDetails)467         public DataStallReport(
468                 @NonNull Network network,
469                 long reportTimestamp,
470                 @DetectionMethod int detectionMethod,
471                 @NonNull LinkProperties linkProperties,
472                 @NonNull NetworkCapabilities networkCapabilities,
473                 @NonNull PersistableBundle stallDetails) {
474             mNetwork = network;
475             mReportTimestamp = reportTimestamp;
476             mDetectionMethod = detectionMethod;
477             mLinkProperties = new LinkProperties(linkProperties);
478             mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
479             mStallDetails = stallDetails;
480         }
481 
482         /**
483          * Returns the Network for this DataStallReport.
484          *
485          * @return The Network for which this DataStallReport applied
486          */
487         @NonNull
getNetwork()488         public Network getNetwork() {
489             return mNetwork;
490         }
491 
492         /**
493          * Returns the epoch timestamp (milliseconds) for when this report was taken.
494          *
495          * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
496          */
getReportTimestamp()497         public long getReportTimestamp() {
498             return mReportTimestamp;
499         }
500 
501         /**
502          * Returns the bitmask of detection methods used to identify this suspected data stall.
503          *
504          * @return The bitmask of detection methods used to identify the suspected data stall
505          */
getDetectionMethod()506         public int getDetectionMethod() {
507             return mDetectionMethod;
508         }
509 
510         /**
511          * Returns the LinkProperties available when this report was taken.
512          *
513          * @return LinkProperties available on the Network at the reported timestamp
514          */
515         @NonNull
getLinkProperties()516         public LinkProperties getLinkProperties() {
517             return new LinkProperties(mLinkProperties);
518         }
519 
520         /**
521          * Returns the NetworkCapabilities when this report was taken.
522          *
523          * @return NetworkCapabilities available on the Network at the reported timestamp
524          */
525         @NonNull
getNetworkCapabilities()526         public NetworkCapabilities getNetworkCapabilities() {
527             return new NetworkCapabilities(mNetworkCapabilities);
528         }
529 
530         /**
531          * Returns a PersistableBundle with additional info for this report.
532          *
533          * <p>Gets a bundle with details about the suspected data stall including information
534          * specific to the monitoring method that detected the data stall.
535          *
536          * @return PersistableBundle that may contain additional information on the suspected data
537          *     stall
538          */
539         @NonNull
getStallDetails()540         public PersistableBundle getStallDetails() {
541             return new PersistableBundle(mStallDetails);
542         }
543 
544         @Override
equals(@ullable Object o)545         public boolean equals(@Nullable Object o) {
546             if (this == o) return true;
547             if (!(o instanceof DataStallReport)) return false;
548             final DataStallReport that = (DataStallReport) o;
549 
550             // PersistableBundle is optimized to avoid unparcelling data unless fields are
551             // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
552             // {@link PersistableBundle#kindofEquals}.
553             return mReportTimestamp == that.mReportTimestamp
554                     && mDetectionMethod == that.mDetectionMethod
555                     && mNetwork.equals(that.mNetwork)
556                     && mLinkProperties.equals(that.mLinkProperties)
557                     && mNetworkCapabilities.equals(that.mNetworkCapabilities)
558                     && persistableBundleEquals(mStallDetails, that.mStallDetails);
559         }
560 
561         @Override
hashCode()562         public int hashCode() {
563             return Objects.hash(
564                     mNetwork,
565                     mReportTimestamp,
566                     mDetectionMethod,
567                     mLinkProperties,
568                     mNetworkCapabilities,
569                     mStallDetails);
570         }
571 
572         /** {@inheritDoc} */
573         @Override
describeContents()574         public int describeContents() {
575             return 0;
576         }
577 
578         /** {@inheritDoc} */
579         @Override
writeToParcel(@onNull Parcel dest, int flags)580         public void writeToParcel(@NonNull Parcel dest, int flags) {
581             dest.writeParcelable(mNetwork, flags);
582             dest.writeLong(mReportTimestamp);
583             dest.writeInt(mDetectionMethod);
584             dest.writeParcelable(mLinkProperties, flags);
585             dest.writeParcelable(mNetworkCapabilities, flags);
586             dest.writeParcelable(mStallDetails, flags);
587         }
588 
589         /** Implement the Parcelable interface */
590         public static final @NonNull Creator<DataStallReport> CREATOR =
591                 new Creator<DataStallReport>() {
592                     public DataStallReport createFromParcel(Parcel in) {
593                         return new DataStallReport(
594                                 in.readParcelable(null),
595                                 in.readLong(),
596                                 in.readInt(),
597                                 in.readParcelable(null),
598                                 in.readParcelable(null),
599                                 in.readParcelable(null));
600                     }
601 
602                     public DataStallReport[] newArray(int size) {
603                         return new DataStallReport[size];
604                     }
605                 };
606     }
607 
608     /** @hide */
609     @VisibleForTesting
610     public static class ConnectivityDiagnosticsBinder
611             extends IConnectivityDiagnosticsCallback.Stub {
612         @NonNull private final ConnectivityDiagnosticsCallback mCb;
613         @NonNull private final Executor mExecutor;
614 
615         /** @hide */
616         @VisibleForTesting
ConnectivityDiagnosticsBinder( @onNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor)617         public ConnectivityDiagnosticsBinder(
618                 @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) {
619             this.mCb = cb;
620             this.mExecutor = executor;
621         }
622 
623         /** @hide */
624         @VisibleForTesting
onConnectivityReportAvailable(@onNull ConnectivityReport report)625         public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
626             Binder.withCleanCallingIdentity(() -> {
627                 mExecutor.execute(() -> {
628                     mCb.onConnectivityReportAvailable(report);
629                 });
630             });
631         }
632 
633         /** @hide */
634         @VisibleForTesting
onDataStallSuspected(@onNull DataStallReport report)635         public void onDataStallSuspected(@NonNull DataStallReport report) {
636             Binder.withCleanCallingIdentity(() -> {
637                 mExecutor.execute(() -> {
638                     mCb.onDataStallSuspected(report);
639                 });
640             });
641         }
642 
643         /** @hide */
644         @VisibleForTesting
onNetworkConnectivityReported( @onNull Network network, boolean hasConnectivity)645         public void onNetworkConnectivityReported(
646                 @NonNull Network network, boolean hasConnectivity) {
647             Binder.withCleanCallingIdentity(() -> {
648                 mExecutor.execute(() -> {
649                     mCb.onNetworkConnectivityReported(network, hasConnectivity);
650                 });
651             });
652         }
653     }
654 
655     /**
656      * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
657      * network connectivity events. Must be extended by applications wanting notifications.
658      */
659     public abstract static class ConnectivityDiagnosticsCallback {
660         /**
661          * Called when the platform completes a data connectivity check. This will also be invoked
662          * immediately upon registration for each network matching the request with the latest
663          * report, if a report has already been generated for that network.
664          *
665          * <p>The Network specified in the ConnectivityReport may not be active any more when this
666          * method is invoked.
667          *
668          * @param report The ConnectivityReport containing information about a connectivity check
669          */
onConnectivityReportAvailable(@onNull ConnectivityReport report)670         public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
671 
672         /**
673          * Called when the platform suspects a data stall on some Network.
674          *
675          * <p>The Network specified in the DataStallReport may not be active any more when this
676          * method is invoked.
677          *
678          * @param report The DataStallReport containing information about the suspected data stall
679          */
onDataStallSuspected(@onNull DataStallReport report)680         public void onDataStallSuspected(@NonNull DataStallReport report) {}
681 
682         /**
683          * Called when any app reports connectivity to the System.
684          *
685          * @param network The Network for which connectivity has been reported
686          * @param hasConnectivity The connectivity reported to the System
687          */
onNetworkConnectivityReported( @onNull Network network, boolean hasConnectivity)688         public void onNetworkConnectivityReported(
689                 @NonNull Network network, boolean hasConnectivity) {}
690     }
691 
692     /**
693      * Registers a ConnectivityDiagnosticsCallback with the System.
694      *
695      * <p>Only apps that offer network connectivity to the user should be registering callbacks.
696      * These are the only apps whose callbacks will be invoked by the system. Apps considered to
697      * meet these conditions include:
698      *
699      * <ul>
700      *   <li>Carrier apps with active subscriptions
701      *   <li>Active VPNs
702      *   <li>WiFi Suggesters
703      * </ul>
704      *
705      * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
706      *
707      * <p>If a registering app loses its relevant permissions, any callbacks it registered will
708      * silently stop receiving callbacks.
709      *
710      * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
711      * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
712      * multiple NetworkRequests, an IllegalArgumentException will be thrown.
713      *
714      * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
715      * number of outstanding requests to 100 per app (identified by their UID), shared with
716      * callbacks in {@link ConnectivityManager}. Registering a callback with this method will count
717      * toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting
718      * this issue and to conserve resources, make sure to unregister the callbacks with
719      * {@link #unregisterConnectivityDiagnosticsCallback}.
720      *
721      * @param request The NetworkRequest that will be used to match with Networks for which
722      *     callbacks will be fired
723      * @param e The Executor to be used for running the callback method invocations
724      * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the
725      *     System
726      * @throws IllegalArgumentException if the same callback instance is registered with multiple
727      *     NetworkRequests
728      * @throws RuntimeException if the app already has too many callbacks registered.
729      */
registerConnectivityDiagnosticsCallback( @onNull NetworkRequest request, @NonNull Executor e, @NonNull ConnectivityDiagnosticsCallback callback)730     public void registerConnectivityDiagnosticsCallback(
731             @NonNull NetworkRequest request,
732             @NonNull Executor e,
733             @NonNull ConnectivityDiagnosticsCallback callback) {
734         final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
735         if (sCallbacks.putIfAbsent(callback, binder) != null) {
736             throw new IllegalArgumentException("Callback is currently registered");
737         }
738 
739         try {
740             mService.registerConnectivityDiagnosticsCallback(
741                     binder, request, mContext.getOpPackageName());
742         } catch (RemoteException exception) {
743             exception.rethrowFromSystemServer();
744         }
745     }
746 
747     /**
748      * Unregisters a ConnectivityDiagnosticsCallback with the System.
749      *
750      * <p>If the given callback is not currently registered with the System, this operation will be
751      * a no-op.
752      *
753      * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System.
754      */
unregisterConnectivityDiagnosticsCallback( @onNull ConnectivityDiagnosticsCallback callback)755     public void unregisterConnectivityDiagnosticsCallback(
756             @NonNull ConnectivityDiagnosticsCallback callback) {
757         // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
758         // atomic.
759         final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
760         if (binder == null) return;
761 
762         try {
763             mService.unregisterConnectivityDiagnosticsCallback(binder);
764         } catch (RemoteException exception) {
765             exception.rethrowFromSystemServer();
766         }
767     }
768 }
769