blob: 523449497345a02f14bae428a8ca559a04bf9515 [file] [log] [blame]
Remi NGUYEN VANfbbccbc2021-01-15 18:08:24 +09001/*
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
17package android.net;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.StringDef;
23import android.content.Context;
24import android.os.Binder;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.os.PersistableBundle;
28import android.os.RemoteException;
29
30import com.android.internal.annotations.VisibleForTesting;
31import com.android.internal.util.Preconditions;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.util.Map;
36import java.util.Objects;
37import java.util.concurrent.ConcurrentHashMap;
38import 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 */
62public 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 */
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
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 */
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
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 */
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
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
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
305 public PersistableBundle getAdditionalInfo() {
306 return new PersistableBundle(mAdditionalInfo);
307 }
308
309 @Override
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
326 public int hashCode() {
327 return Objects.hash(
328 mNetwork,
329 mReportTimestamp,
330 mLinkProperties,
331 mNetworkCapabilities,
332 mAdditionalInfo);
333 }
334
335 /** {@inheritDoc} */
336 @Override
337 public int describeContents() {
338 return 0;
339 }
340
341 /** {@inheritDoc} */
342 @Override
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 */
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
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 */
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 */
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
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
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
540 public PersistableBundle getStallDetails() {
541 return new PersistableBundle(mStallDetails);
542 }
543
544 @Override
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
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
574 public int describeContents() {
575 return 0;
576 }
577
578 /** {@inheritDoc} */
579 @Override
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
617 public ConnectivityDiagnosticsBinder(
618 @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) {
619 this.mCb = cb;
620 this.mExecutor = executor;
621 }
622
623 /** @hide */
624 @VisibleForTesting
625 public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
626 final long token = Binder.clearCallingIdentity();
627 try {
628 mExecutor.execute(() -> {
629 mCb.onConnectivityReportAvailable(report);
630 });
631 } finally {
632 Binder.restoreCallingIdentity(token);
633 }
634 }
635
636 /** @hide */
637 @VisibleForTesting
638 public void onDataStallSuspected(@NonNull DataStallReport report) {
639 final long token = Binder.clearCallingIdentity();
640 try {
641 mExecutor.execute(() -> {
642 mCb.onDataStallSuspected(report);
643 });
644 } finally {
645 Binder.restoreCallingIdentity(token);
646 }
647 }
648
649 /** @hide */
650 @VisibleForTesting
651 public void onNetworkConnectivityReported(
652 @NonNull Network network, boolean hasConnectivity) {
653 final long token = Binder.clearCallingIdentity();
654 try {
655 mExecutor.execute(() -> {
656 mCb.onNetworkConnectivityReported(network, hasConnectivity);
657 });
658 } finally {
659 Binder.restoreCallingIdentity(token);
660 }
661 }
662 }
663
664 /**
665 * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
666 * network connectivity events. Must be extended by applications wanting notifications.
667 */
668 public abstract static class ConnectivityDiagnosticsCallback {
669 /**
670 * Called when the platform completes a data connectivity check. This will also be invoked
671 * immediately upon registration for each network matching the request with the latest
672 * report, if a report has already been generated for that network.
673 *
674 * <p>The Network specified in the ConnectivityReport may not be active any more when this
675 * method is invoked.
676 *
677 * @param report The ConnectivityReport containing information about a connectivity check
678 */
679 public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
680
681 /**
682 * Called when the platform suspects a data stall on some Network.
683 *
684 * <p>The Network specified in the DataStallReport may not be active any more when this
685 * method is invoked.
686 *
687 * @param report The DataStallReport containing information about the suspected data stall
688 */
689 public void onDataStallSuspected(@NonNull DataStallReport report) {}
690
691 /**
692 * Called when any app reports connectivity to the System.
693 *
694 * @param network The Network for which connectivity has been reported
695 * @param hasConnectivity The connectivity reported to the System
696 */
697 public void onNetworkConnectivityReported(
698 @NonNull Network network, boolean hasConnectivity) {}
699 }
700
701 /**
702 * Registers a ConnectivityDiagnosticsCallback with the System.
703 *
704 * <p>Only apps that offer network connectivity to the user should be registering callbacks.
705 * These are the only apps whose callbacks will be invoked by the system. Apps considered to
706 * meet these conditions include:
707 *
708 * <ul>
709 * <li>Carrier apps with active subscriptions
710 * <li>Active VPNs
711 * <li>WiFi Suggesters
712 * </ul>
713 *
714 * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
715 *
716 * <p>If a registering app loses its relevant permissions, any callbacks it registered will
717 * silently stop receiving callbacks.
718 *
719 * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
720 * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
721 * multiple NetworkRequests, an IllegalArgumentException will be thrown.
722 *
723 * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
724 * number of outstanding requests to 100 per app (identified by their UID), shared with
725 * callbacks in {@link ConnectivityManager}. Registering a callback with this method will count
726 * toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting
727 * this issue and to conserve resources, make sure to unregister the callbacks with
728 * {@link #unregisterConnectivityDiagnosticsCallback}.
729 *
730 * @param request The NetworkRequest that will be used to match with Networks for which
731 * callbacks will be fired
732 * @param e The Executor to be used for running the callback method invocations
733 * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the
734 * System
735 * @throws IllegalArgumentException if the same callback instance is registered with multiple
736 * NetworkRequests
737 * @throws RuntimeException if the app already has too many callbacks registered.
738 */
739 public void registerConnectivityDiagnosticsCallback(
740 @NonNull NetworkRequest request,
741 @NonNull Executor e,
742 @NonNull ConnectivityDiagnosticsCallback callback) {
743 final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
744 if (sCallbacks.putIfAbsent(callback, binder) != null) {
745 throw new IllegalArgumentException("Callback is currently registered");
746 }
747
748 try {
749 mService.registerConnectivityDiagnosticsCallback(
750 binder, request, mContext.getOpPackageName());
751 } catch (RemoteException exception) {
752 exception.rethrowFromSystemServer();
753 }
754 }
755
756 /**
757 * Unregisters a ConnectivityDiagnosticsCallback with the System.
758 *
759 * <p>If the given callback is not currently registered with the System, this operation will be
760 * a no-op.
761 *
762 * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System.
763 */
764 public void unregisterConnectivityDiagnosticsCallback(
765 @NonNull ConnectivityDiagnosticsCallback callback) {
766 // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
767 // atomic.
768 final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
769 if (binder == null) return;
770
771 try {
772 mService.unregisterConnectivityDiagnosticsCallback(binder);
773 } catch (RemoteException exception) {
774 exception.rethrowFromSystemServer();
775 }
776 }
777}