blob: dcc8a5eacd1386e03688035e54f68465ef25fe0b [file] [log] [blame]
Cody Kesting8ddcb9f2019-12-11 08:34:51 -08001/*
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;
Cody Kesting8c0043e2019-12-17 08:51:32 -080021import android.annotation.Nullable;
Cody Kestingbfc074592020-01-20 18:03:41 -080022import android.annotation.StringDef;
Cody Kestinga5e2cef2019-12-17 17:21:40 -080023import android.content.Context;
Cody Kestingf259e352019-12-17 16:46:11 -080024import android.os.Binder;
Cody Kesting8c0043e2019-12-17 08:51:32 -080025import android.os.Parcel;
26import android.os.Parcelable;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080027import android.os.PersistableBundle;
Cody Kesting0ffbf922019-12-18 10:57:50 -080028import android.os.RemoteException;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080029
Cody Kesting8c0043e2019-12-17 08:51:32 -080030import com.android.internal.annotations.VisibleForTesting;
31
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080032import java.lang.annotation.Retention;
33import java.lang.annotation.RetentionPolicy;
Cody Kesting0ffbf922019-12-18 10:57:50 -080034import java.util.Map;
Cody Kesting8c0043e2019-12-17 08:51:32 -080035import java.util.Objects;
Cody Kesting0ffbf922019-12-18 10:57:50 -080036import java.util.concurrent.ConcurrentHashMap;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080037import 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 */
61public class ConnectivityDiagnosticsManager {
Cody Kesting0ffbf922019-12-18 10:57:50 -080062 /** @hide */
63 @VisibleForTesting
64 public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
65 sCallbacks = new ConcurrentHashMap<>();
66
Cody Kestinga5e2cef2019-12-17 17:21:40 -080067 private final Context mContext;
68 private final IConnectivityManager mService;
69
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080070 /** @hide */
Cody Kestinga5e2cef2019-12-17 17:21:40 -080071 public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
Remi NGUYEN VANacd6de12021-03-15 07:31:54 +000072 mContext = Objects.requireNonNull(context, "missing context");
73 mService = Objects.requireNonNull(service, "missing IConnectivityManager");
Cody Kestinga5e2cef2019-12-17 17:21:40 -080074 }
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080075
Cody Kesting8c0043e2019-12-17 08:51:32 -080076 /** @hide */
77 @VisibleForTesting
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
Cody Kesting8ddcb9f2019-12-11 08:34:51 -080089 /** Class that includes connectivity information for a specific Network at a specific time. */
Cody Kesting8c0043e2019-12-17 08:51:32 -080090 public static final class ConnectivityReport implements Parcelable {
Cody Kestingbfc074592020-01-20 18:03:41 -080091 /**
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 *
Cody Kestingc5941bb2020-02-05 10:33:55 -0800138 * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
Cody Kestingbfc074592020-01-20 18:03:41 -0800139 */
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 =
Cody Kestingc52e5aa2020-02-05 08:53:03 -0800204 "networkProbesAttempted";
Cody Kestingbfc074592020-01-20 18:03:41 -0800205
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
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800213 /** The Network for which this ConnectivityReport applied */
Cody Kesting8c0043e2019-12-17 08:51:32 -0800214 @NonNull private final Network mNetwork;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800215
216 /**
217 * The timestamp for the report. The timestamp is taken from {@link
218 * System#currentTimeMillis}.
219 */
Cody Kesting8c0043e2019-12-17 08:51:32 -0800220 private final long mReportTimestamp;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800221
222 /** LinkProperties available on the Network at the reported timestamp */
Cody Kesting8c0043e2019-12-17 08:51:32 -0800223 @NonNull private final LinkProperties mLinkProperties;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800224
225 /** NetworkCapabilities available on the Network at the reported timestamp */
Cody Kesting8c0043e2019-12-17 08:51:32 -0800226 @NonNull private final NetworkCapabilities mNetworkCapabilities;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800227
228 /** PersistableBundle that may contain additional info about the report */
Cody Kesting8c0043e2019-12-17 08:51:32 -0800229 @NonNull private final PersistableBundle mAdditionalInfo;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800230
231 /**
232 * Constructor for ConnectivityReport.
233 *
234 * <p>Apps should obtain instances through {@link
Cody Kestinge4d2df52020-03-05 15:19:48 -0800235 * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
236 * their own instances (unless for testing purposes).
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800237 *
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 */
246 public ConnectivityReport(
247 @NonNull Network network,
248 long reportTimestamp,
249 @NonNull LinkProperties linkProperties,
250 @NonNull NetworkCapabilities networkCapabilities,
251 @NonNull PersistableBundle additionalInfo) {
Cody Kesting8c0043e2019-12-17 08:51:32 -0800252 mNetwork = network;
253 mReportTimestamp = reportTimestamp;
Cody Kesting4a773072020-02-04 21:52:09 -0800254 mLinkProperties = new LinkProperties(linkProperties);
255 mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
Cody Kesting8c0043e2019-12-17 08:51:32 -0800256 mAdditionalInfo = additionalInfo;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800257 }
Cody Kesting8c0043e2019-12-17 08:51:32 -0800258
259 /**
260 * Returns the Network for this ConnectivityReport.
261 *
262 * @return The Network for which this ConnectivityReport applied
263 */
264 @NonNull
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 */
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
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
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
304 public PersistableBundle getAdditionalInfo() {
305 return new PersistableBundle(mAdditionalInfo);
306 }
307
308 @Override
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
325 public int hashCode() {
326 return Objects.hash(
327 mNetwork,
328 mReportTimestamp,
329 mLinkProperties,
330 mNetworkCapabilities,
331 mAdditionalInfo);
332 }
333
334 /** {@inheritDoc} */
335 @Override
336 public int describeContents() {
337 return 0;
338 }
339
340 /** {@inheritDoc} */
341 @Override
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 =
Cody Kesting27bf53f2020-01-22 20:14:48 -0800352 new Creator<ConnectivityReport>() {
Cody Kesting8c0043e2019-12-17 08:51:32 -0800353 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 };
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800366 }
367
368 /** Class that includes information for a suspected data stall on a specific Network */
Cody Kesting525fe052019-12-17 09:28:06 -0800369 public static final class DataStallReport implements Parcelable {
Cody Kesting950f4ef2020-03-02 08:43:24 -0800370 /**
371 * Indicates that the Data Stall was detected using DNS events.
372 */
Cody Kesting525fe052019-12-17 09:28:06 -0800373 public static final int DETECTION_METHOD_DNS_EVENTS = 1;
Cody Kesting950f4ef2020-03-02 08:43:24 -0800374
375 /**
376 * Indicates that the Data Stall was detected using TCP metrics.
377 */
Cody Kesting525fe052019-12-17 09:28:06 -0800378 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
Cody Kestingbfc074592020-01-20 18:03:41 -0800387 /**
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
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800430 /** The Network for which this DataStallReport applied */
Cody Kesting525fe052019-12-17 09:28:06 -0800431 @NonNull private final Network mNetwork;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800432
433 /**
434 * The timestamp for the report. The timestamp is taken from {@link
435 * System#currentTimeMillis}.
436 */
Cody Kesting525fe052019-12-17 09:28:06 -0800437 private long mReportTimestamp;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800438
Cody Kesting43a47ef2020-05-15 10:36:01 -0700439 /** A bitmask of the detection methods used to identify the suspected data stall */
Cody Kesting525fe052019-12-17 09:28:06 -0800440 @DetectionMethod private final int mDetectionMethod;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800441
Cody Kesting4a773072020-02-04 21:52:09 -0800442 /** 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
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800448 /** PersistableBundle that may contain additional information on the suspected data stall */
Cody Kesting525fe052019-12-17 09:28:06 -0800449 @NonNull private final PersistableBundle mStallDetails;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800450
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
Cody Kesting4a773072020-02-04 21:52:09 -0800461 * @param linkProperties The LinkProperties available on network at reportTimestamp
462 * @param networkCapabilities The NetworkCapabilities available on network at
463 * reportTimestamp
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800464 * @param stallDetails A PersistableBundle that may contain additional info about the report
465 */
466 public DataStallReport(
467 @NonNull Network network,
468 long reportTimestamp,
469 @DetectionMethod int detectionMethod,
Cody Kesting4a773072020-02-04 21:52:09 -0800470 @NonNull LinkProperties linkProperties,
471 @NonNull NetworkCapabilities networkCapabilities,
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800472 @NonNull PersistableBundle stallDetails) {
Cody Kesting525fe052019-12-17 09:28:06 -0800473 mNetwork = network;
474 mReportTimestamp = reportTimestamp;
475 mDetectionMethod = detectionMethod;
Cody Kesting4a773072020-02-04 21:52:09 -0800476 mLinkProperties = new LinkProperties(linkProperties);
477 mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
Cody Kesting525fe052019-12-17 09:28:06 -0800478 mStallDetails = stallDetails;
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800479 }
Cody Kesting525fe052019-12-17 09:28:06 -0800480
481 /**
482 * Returns the Network for this DataStallReport.
483 *
484 * @return The Network for which this DataStallReport applied
485 */
486 @NonNull
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 */
496 public long getReportTimestamp() {
497 return mReportTimestamp;
498 }
499
500 /**
Cody Kesting43a47ef2020-05-15 10:36:01 -0700501 * Returns the bitmask of detection methods used to identify this suspected data stall.
Cody Kesting525fe052019-12-17 09:28:06 -0800502 *
Cody Kesting43a47ef2020-05-15 10:36:01 -0700503 * @return The bitmask of detection methods used to identify the suspected data stall
Cody Kesting525fe052019-12-17 09:28:06 -0800504 */
505 public int getDetectionMethod() {
506 return mDetectionMethod;
507 }
508
509 /**
Cody Kesting4a773072020-02-04 21:52:09 -0800510 * Returns the LinkProperties available when this report was taken.
511 *
512 * @return LinkProperties available on the Network at the reported timestamp
513 */
514 @NonNull
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
525 public NetworkCapabilities getNetworkCapabilities() {
526 return new NetworkCapabilities(mNetworkCapabilities);
527 }
528
529 /**
Cody Kesting525fe052019-12-17 09:28:06 -0800530 * Returns a PersistableBundle with additional info for this report.
531 *
Cody Kestingbfc074592020-01-20 18:03:41 -0800532 * <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 *
Cody Kesting525fe052019-12-17 09:28:06 -0800535 * @return PersistableBundle that may contain additional information on the suspected data
536 * stall
537 */
538 @NonNull
539 public PersistableBundle getStallDetails() {
540 return new PersistableBundle(mStallDetails);
541 }
542
543 @Override
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)
Cody Kesting4a773072020-02-04 21:52:09 -0800555 && mLinkProperties.equals(that.mLinkProperties)
556 && mNetworkCapabilities.equals(that.mNetworkCapabilities)
Cody Kesting525fe052019-12-17 09:28:06 -0800557 && persistableBundleEquals(mStallDetails, that.mStallDetails);
558 }
559
560 @Override
561 public int hashCode() {
Cody Kesting4a773072020-02-04 21:52:09 -0800562 return Objects.hash(
563 mNetwork,
564 mReportTimestamp,
565 mDetectionMethod,
566 mLinkProperties,
567 mNetworkCapabilities,
568 mStallDetails);
Cody Kesting525fe052019-12-17 09:28:06 -0800569 }
570
571 /** {@inheritDoc} */
572 @Override
573 public int describeContents() {
574 return 0;
575 }
576
577 /** {@inheritDoc} */
578 @Override
579 public void writeToParcel(@NonNull Parcel dest, int flags) {
580 dest.writeParcelable(mNetwork, flags);
581 dest.writeLong(mReportTimestamp);
582 dest.writeInt(mDetectionMethod);
Cody Kesting4a773072020-02-04 21:52:09 -0800583 dest.writeParcelable(mLinkProperties, flags);
584 dest.writeParcelable(mNetworkCapabilities, flags);
Cody Kesting525fe052019-12-17 09:28:06 -0800585 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(),
Cody Kesting4a773072020-02-04 21:52:09 -0800596 in.readParcelable(null),
597 in.readParcelable(null),
Cody Kesting525fe052019-12-17 09:28:06 -0800598 in.readParcelable(null));
599 }
600
601 public DataStallReport[] newArray(int size) {
602 return new DataStallReport[size];
603 }
604 };
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800605 }
606
Cody Kestingf259e352019-12-17 16:46:11 -0800607 /** @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
616 public ConnectivityDiagnosticsBinder(
617 @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) {
618 this.mCb = cb;
619 this.mExecutor = executor;
620 }
621
622 /** @hide */
623 @VisibleForTesting
Cody Kestinge4d2df52020-03-05 15:19:48 -0800624 public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
lucaslinad369e32020-12-30 11:54:55 +0800625 final long token = Binder.clearCallingIdentity();
626 try {
Cody Kestingf259e352019-12-17 16:46:11 -0800627 mExecutor.execute(() -> {
Cody Kestinge4d2df52020-03-05 15:19:48 -0800628 mCb.onConnectivityReportAvailable(report);
Cody Kestingf259e352019-12-17 16:46:11 -0800629 });
lucaslinad369e32020-12-30 11:54:55 +0800630 } finally {
631 Binder.restoreCallingIdentity(token);
632 }
Cody Kestingf259e352019-12-17 16:46:11 -0800633 }
634
635 /** @hide */
636 @VisibleForTesting
637 public void onDataStallSuspected(@NonNull DataStallReport report) {
lucaslinad369e32020-12-30 11:54:55 +0800638 final long token = Binder.clearCallingIdentity();
639 try {
Cody Kestingf259e352019-12-17 16:46:11 -0800640 mExecutor.execute(() -> {
641 mCb.onDataStallSuspected(report);
642 });
lucaslinad369e32020-12-30 11:54:55 +0800643 } finally {
644 Binder.restoreCallingIdentity(token);
645 }
Cody Kestingf259e352019-12-17 16:46:11 -0800646 }
647
648 /** @hide */
649 @VisibleForTesting
650 public void onNetworkConnectivityReported(
651 @NonNull Network network, boolean hasConnectivity) {
lucaslinad369e32020-12-30 11:54:55 +0800652 final long token = Binder.clearCallingIdentity();
653 try {
Cody Kestingf259e352019-12-17 16:46:11 -0800654 mExecutor.execute(() -> {
655 mCb.onNetworkConnectivityReported(network, hasConnectivity);
656 });
lucaslinad369e32020-12-30 11:54:55 +0800657 } finally {
658 Binder.restoreCallingIdentity(token);
659 }
Cody Kestingf259e352019-12-17 16:46:11 -0800660 }
661 }
662
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800663 /**
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
Cody Kesting782d0a02020-03-30 12:03:21 -0700670 * immediately upon registration for each network matching the request with the latest
671 * report, if a report has already been generated for that network.
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800672 *
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 */
Cody Kestinge4d2df52020-03-05 15:19:48 -0800678 public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800679
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 */
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 */
696 public void onNetworkConnectivityReported(
697 @NonNull Network network, boolean hasConnectivity) {}
698 }
699
700 /**
701 * Registers a ConnectivityDiagnosticsCallback with the System.
702 *
Cody Kesting6d8560d2019-12-19 12:36:18 -0800703 * <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:
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800706 *
707 * <ul>
708 * <li>Carrier apps with active subscriptions
709 * <li>Active VPNs
710 * <li>WiFi Suggesters
711 * </ul>
712 *
Cody Kesting6d8560d2019-12-19 12:36:18 -0800713 * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800714 *
715 * <p>If a registering app loses its relevant permissions, any callbacks it registered will
Cody Kesting5b2d7ca2021-05-05 13:17:22 -0700716 * 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).
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800719 *
Cody Kesting0ffbf922019-12-18 10:57:50 -0800720 * <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.
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800723 *
Chalard Jean7f06b342020-05-20 16:11:50 +0900724 * <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 *
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800731 * @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
Chalard Jean7f06b342020-05-20 16:11:50 +0900738 * @throws RuntimeException if the app already has too many callbacks registered.
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800739 */
740 public void registerConnectivityDiagnosticsCallback(
741 @NonNull NetworkRequest request,
742 @NonNull Executor e,
743 @NonNull ConnectivityDiagnosticsCallback callback) {
Cody Kesting0ffbf922019-12-18 10:57:50 -0800744 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 {
Cody Kestingd292a332020-01-05 14:06:39 -0800750 mService.registerConnectivityDiagnosticsCallback(
751 binder, request, mContext.getOpPackageName());
Cody Kesting0ffbf922019-12-18 10:57:50 -0800752 } catch (RemoteException exception) {
753 exception.rethrowFromSystemServer();
754 }
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800755 }
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 */
765 public void unregisterConnectivityDiagnosticsCallback(
766 @NonNull ConnectivityDiagnosticsCallback callback) {
Cody Kesting0ffbf922019-12-18 10:57:50 -0800767 // 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 }
Cody Kesting8ddcb9f2019-12-11 08:34:51 -0800777 }
778}