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