| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.StringDef; |
| import android.content.Context; |
| import android.os.Binder; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.PersistableBundle; |
| import android.os.RemoteException; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.Executor; |
| |
| /** |
| * Class that provides utilities for collecting network connectivity diagnostics information. |
| * Connectivity information is made available through triggerable diagnostics tools and by listening |
| * to System validations. Some diagnostics information may be permissions-restricted. |
| * |
| * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network |
| * connectivity on a user device. These tools will provide several mechanisms for these applications |
| * to be alerted to network conditions as well as diagnose potential network issues themselves. |
| * |
| * <p>The primary responsibilities of this class are to: |
| * |
| * <ul> |
| * <li>Allow permissioned applications to register and unregister callbacks for network event |
| * notifications |
| * <li>Invoke callbacks for network event notifications, including: |
| * <ul> |
| * <li>Network validations |
| * <li>Data stalls |
| * <li>Connectivity reports from applications |
| * </ul> |
| * </ul> |
| */ |
| public class ConnectivityDiagnosticsManager { |
| /** @hide */ |
| @VisibleForTesting |
| public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder> |
| sCallbacks = new ConcurrentHashMap<>(); |
| |
| private final Context mContext; |
| private final IConnectivityManager mService; |
| |
| /** @hide */ |
| public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) { |
| mContext = Objects.requireNonNull(context, "missing context"); |
| mService = Objects.requireNonNull(service, "missing IConnectivityManager"); |
| } |
| |
| /** @hide */ |
| @VisibleForTesting |
| public static boolean persistableBundleEquals( |
| @Nullable PersistableBundle a, @Nullable PersistableBundle b) { |
| if (a == b) return true; |
| if (a == null || b == null) return false; |
| if (!Objects.equals(a.keySet(), b.keySet())) return false; |
| for (String key : a.keySet()) { |
| if (!Objects.equals(a.get(key), b.get(key))) return false; |
| } |
| return true; |
| } |
| |
| /** Class that includes connectivity information for a specific Network at a specific time. */ |
| public static final class ConnectivityReport implements Parcelable { |
| /** |
| * The overall status of the network is that it is invalid; it neither provides |
| * connectivity nor has been exempted from validation. |
| */ |
| public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; |
| |
| /** |
| * The overall status of the network is that it is valid, this may be because it provides |
| * full Internet access (all probes succeeded), or because other properties of the network |
| * caused probes not to be run. |
| */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID |
| public static final int NETWORK_VALIDATION_RESULT_VALID = 1; |
| |
| /** |
| * The overall status of the network is that it provides partial connectivity; some |
| * probed services succeeded but others failed. |
| */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; |
| public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; |
| |
| /** |
| * Due to the properties of the network, validation was not performed. |
| */ |
| public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; |
| |
| /** @hide */ |
| @IntDef( |
| prefix = {"NETWORK_VALIDATION_RESULT_"}, |
| value = { |
| NETWORK_VALIDATION_RESULT_INVALID, |
| NETWORK_VALIDATION_RESULT_VALID, |
| NETWORK_VALIDATION_RESULT_PARTIALLY_VALID, |
| NETWORK_VALIDATION_RESULT_SKIPPED |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface NetworkValidationResult {} |
| |
| /** |
| * The overall validation result for the Network being reported on. |
| * |
| * <p>The possible values for this key are: |
| * {@link #NETWORK_VALIDATION_RESULT_INVALID}, |
| * {@link #NETWORK_VALIDATION_RESULT_VALID}, |
| * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID}, |
| * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}. |
| * |
| * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED |
| */ |
| @NetworkValidationResult |
| public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; |
| |
| /** DNS probe. */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS |
| public static final int NETWORK_PROBE_DNS = 0x04; |
| |
| /** HTTP probe. */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP |
| public static final int NETWORK_PROBE_HTTP = 0x08; |
| |
| /** HTTPS probe. */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; |
| public static final int NETWORK_PROBE_HTTPS = 0x10; |
| |
| /** Captive portal fallback probe. */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK |
| public static final int NETWORK_PROBE_FALLBACK = 0x20; |
| |
| /** Private DNS (DNS over TLS) probd. */ |
| // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS |
| public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40; |
| |
| /** @hide */ |
| @IntDef( |
| prefix = {"NETWORK_PROBE_"}, |
| value = { |
| NETWORK_PROBE_DNS, |
| NETWORK_PROBE_HTTP, |
| NETWORK_PROBE_HTTPS, |
| NETWORK_PROBE_FALLBACK, |
| NETWORK_PROBE_PRIVATE_DNS |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface NetworkProbe {} |
| |
| /** |
| * A bitmask of network validation probes that succeeded. |
| * |
| * <p>The possible bits values reported by this key are: |
| * {@link #NETWORK_PROBE_DNS}, |
| * {@link #NETWORK_PROBE_HTTP}, |
| * {@link #NETWORK_PROBE_HTTPS}, |
| * {@link #NETWORK_PROBE_FALLBACK}, |
| * {@link #NETWORK_PROBE_PRIVATE_DNS}. |
| */ |
| @NetworkProbe |
| public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = |
| "networkProbesSucceeded"; |
| |
| /** |
| * A bitmask of network validation probes that were attempted. |
| * |
| * <p>These probes may have failed or may be incomplete at the time of this report. |
| * |
| * <p>The possible bits values reported by this key are: |
| * {@link #NETWORK_PROBE_DNS}, |
| * {@link #NETWORK_PROBE_HTTP}, |
| * {@link #NETWORK_PROBE_HTTPS}, |
| * {@link #NETWORK_PROBE_FALLBACK}, |
| * {@link #NETWORK_PROBE_PRIVATE_DNS}. |
| */ |
| @NetworkProbe |
| public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = |
| "networkProbesAttempted"; |
| |
| /** @hide */ |
| @StringDef(prefix = {"KEY_"}, value = { |
| KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, |
| KEY_NETWORK_PROBES_ATTEMPTED_BITMASK}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface ConnectivityReportBundleKeys {} |
| |
| /** The Network for which this ConnectivityReport applied */ |
| @NonNull private final Network mNetwork; |
| |
| /** |
| * The timestamp for the report. The timestamp is taken from {@link |
| * System#currentTimeMillis}. |
| */ |
| private final long mReportTimestamp; |
| |
| /** LinkProperties available on the Network at the reported timestamp */ |
| @NonNull private final LinkProperties mLinkProperties; |
| |
| /** NetworkCapabilities available on the Network at the reported timestamp */ |
| @NonNull private final NetworkCapabilities mNetworkCapabilities; |
| |
| /** PersistableBundle that may contain additional info about the report */ |
| @NonNull private final PersistableBundle mAdditionalInfo; |
| |
| /** |
| * Constructor for ConnectivityReport. |
| * |
| * <p>Apps should obtain instances through {@link |
| * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating |
| * their own instances (unless for testing purposes). |
| * |
| * @param network The Network for which this ConnectivityReport applies |
| * @param reportTimestamp The timestamp for the report |
| * @param linkProperties The LinkProperties available on network at reportTimestamp |
| * @param networkCapabilities The NetworkCapabilities available on network at |
| * reportTimestamp |
| * @param additionalInfo A PersistableBundle that may contain additional info about the |
| * report |
| */ |
| public ConnectivityReport( |
| @NonNull Network network, |
| long reportTimestamp, |
| @NonNull LinkProperties linkProperties, |
| @NonNull NetworkCapabilities networkCapabilities, |
| @NonNull PersistableBundle additionalInfo) { |
| mNetwork = network; |
| mReportTimestamp = reportTimestamp; |
| mLinkProperties = new LinkProperties(linkProperties); |
| mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); |
| mAdditionalInfo = additionalInfo; |
| } |
| |
| /** |
| * Returns the Network for this ConnectivityReport. |
| * |
| * @return The Network for which this ConnectivityReport applied |
| */ |
| @NonNull |
| public Network getNetwork() { |
| return mNetwork; |
| } |
| |
| /** |
| * Returns the epoch timestamp (milliseconds) for when this report was taken. |
| * |
| * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}. |
| */ |
| public long getReportTimestamp() { |
| return mReportTimestamp; |
| } |
| |
| /** |
| * Returns the LinkProperties available when this report was taken. |
| * |
| * @return LinkProperties available on the Network at the reported timestamp |
| */ |
| @NonNull |
| public LinkProperties getLinkProperties() { |
| return new LinkProperties(mLinkProperties); |
| } |
| |
| /** |
| * Returns the NetworkCapabilities when this report was taken. |
| * |
| * @return NetworkCapabilities available on the Network at the reported timestamp |
| */ |
| @NonNull |
| public NetworkCapabilities getNetworkCapabilities() { |
| return new NetworkCapabilities(mNetworkCapabilities); |
| } |
| |
| /** |
| * Returns a PersistableBundle with additional info for this report. |
| * |
| * @return PersistableBundle that may contain additional info about the report |
| */ |
| @NonNull |
| public PersistableBundle getAdditionalInfo() { |
| return new PersistableBundle(mAdditionalInfo); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (this == o) return true; |
| if (!(o instanceof ConnectivityReport)) return false; |
| final ConnectivityReport that = (ConnectivityReport) o; |
| |
| // PersistableBundle is optimized to avoid unparcelling data unless fields are |
| // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over |
| // {@link PersistableBundle#kindofEquals}. |
| return mReportTimestamp == that.mReportTimestamp |
| && mNetwork.equals(that.mNetwork) |
| && mLinkProperties.equals(that.mLinkProperties) |
| && mNetworkCapabilities.equals(that.mNetworkCapabilities) |
| && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash( |
| mNetwork, |
| mReportTimestamp, |
| mLinkProperties, |
| mNetworkCapabilities, |
| mAdditionalInfo); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeParcelable(mNetwork, flags); |
| dest.writeLong(mReportTimestamp); |
| dest.writeParcelable(mLinkProperties, flags); |
| dest.writeParcelable(mNetworkCapabilities, flags); |
| dest.writeParcelable(mAdditionalInfo, flags); |
| } |
| |
| /** Implement the Parcelable interface */ |
| public static final @NonNull Creator<ConnectivityReport> CREATOR = |
| new Creator<ConnectivityReport>() { |
| public ConnectivityReport createFromParcel(Parcel in) { |
| return new ConnectivityReport( |
| in.readParcelable(null), |
| in.readLong(), |
| in.readParcelable(null), |
| in.readParcelable(null), |
| in.readParcelable(null)); |
| } |
| |
| public ConnectivityReport[] newArray(int size) { |
| return new ConnectivityReport[size]; |
| } |
| }; |
| } |
| |
| /** Class that includes information for a suspected data stall on a specific Network */ |
| public static final class DataStallReport implements Parcelable { |
| /** |
| * Indicates that the Data Stall was detected using DNS events. |
| */ |
| public static final int DETECTION_METHOD_DNS_EVENTS = 1; |
| |
| /** |
| * Indicates that the Data Stall was detected using TCP metrics. |
| */ |
| public static final int DETECTION_METHOD_TCP_METRICS = 2; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef( |
| prefix = {"DETECTION_METHOD_"}, |
| value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS}) |
| public @interface DetectionMethod {} |
| |
| /** |
| * This key represents the period in milliseconds over which other included TCP metrics |
| * were measured. |
| * |
| * <p>This key will be included if the data stall detection method is |
| * {@link #DETECTION_METHOD_TCP_METRICS}. |
| * |
| * <p>This value is an int. |
| */ |
| public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = |
| "tcpMetricsCollectionPeriodMillis"; |
| |
| /** |
| * This key represents the fail rate of TCP packets when the suspected data stall was |
| * detected. |
| * |
| * <p>This key will be included if the data stall detection method is |
| * {@link #DETECTION_METHOD_TCP_METRICS}. |
| * |
| * <p>This value is an int percentage between 0 and 100. |
| */ |
| public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; |
| |
| /** |
| * This key represents the consecutive number of DNS timeouts that have occurred. |
| * |
| * <p>The consecutive count will be reset any time a DNS response is received. |
| * |
| * <p>This key will be included if the data stall detection method is |
| * {@link #DETECTION_METHOD_DNS_EVENTS}. |
| * |
| * <p>This value is an int. |
| */ |
| public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; |
| |
| /** @hide */ |
| @Retention(RetentionPolicy.SOURCE) |
| @StringDef(prefix = {"KEY_"}, value = { |
| KEY_TCP_PACKET_FAIL_RATE, |
| KEY_DNS_CONSECUTIVE_TIMEOUTS |
| }) |
| public @interface DataStallReportBundleKeys {} |
| |
| /** The Network for which this DataStallReport applied */ |
| @NonNull private final Network mNetwork; |
| |
| /** |
| * The timestamp for the report. The timestamp is taken from {@link |
| * System#currentTimeMillis}. |
| */ |
| private long mReportTimestamp; |
| |
| /** A bitmask of the detection methods used to identify the suspected data stall */ |
| @DetectionMethod private final int mDetectionMethod; |
| |
| /** LinkProperties available on the Network at the reported timestamp */ |
| @NonNull private final LinkProperties mLinkProperties; |
| |
| /** NetworkCapabilities available on the Network at the reported timestamp */ |
| @NonNull private final NetworkCapabilities mNetworkCapabilities; |
| |
| /** PersistableBundle that may contain additional information on the suspected data stall */ |
| @NonNull private final PersistableBundle mStallDetails; |
| |
| /** |
| * Constructor for DataStallReport. |
| * |
| * <p>Apps should obtain instances through {@link |
| * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own |
| * instances (unless for testing purposes). |
| * |
| * @param network The Network for which this DataStallReport applies |
| * @param reportTimestamp The timestamp for the report |
| * @param detectionMethod The detection method used to identify this data stall |
| * @param linkProperties The LinkProperties available on network at reportTimestamp |
| * @param networkCapabilities The NetworkCapabilities available on network at |
| * reportTimestamp |
| * @param stallDetails A PersistableBundle that may contain additional info about the report |
| */ |
| public DataStallReport( |
| @NonNull Network network, |
| long reportTimestamp, |
| @DetectionMethod int detectionMethod, |
| @NonNull LinkProperties linkProperties, |
| @NonNull NetworkCapabilities networkCapabilities, |
| @NonNull PersistableBundle stallDetails) { |
| mNetwork = network; |
| mReportTimestamp = reportTimestamp; |
| mDetectionMethod = detectionMethod; |
| mLinkProperties = new LinkProperties(linkProperties); |
| mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); |
| mStallDetails = stallDetails; |
| } |
| |
| /** |
| * Returns the Network for this DataStallReport. |
| * |
| * @return The Network for which this DataStallReport applied |
| */ |
| @NonNull |
| public Network getNetwork() { |
| return mNetwork; |
| } |
| |
| /** |
| * Returns the epoch timestamp (milliseconds) for when this report was taken. |
| * |
| * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}. |
| */ |
| public long getReportTimestamp() { |
| return mReportTimestamp; |
| } |
| |
| /** |
| * Returns the bitmask of detection methods used to identify this suspected data stall. |
| * |
| * @return The bitmask of detection methods used to identify the suspected data stall |
| */ |
| public int getDetectionMethod() { |
| return mDetectionMethod; |
| } |
| |
| /** |
| * Returns the LinkProperties available when this report was taken. |
| * |
| * @return LinkProperties available on the Network at the reported timestamp |
| */ |
| @NonNull |
| public LinkProperties getLinkProperties() { |
| return new LinkProperties(mLinkProperties); |
| } |
| |
| /** |
| * Returns the NetworkCapabilities when this report was taken. |
| * |
| * @return NetworkCapabilities available on the Network at the reported timestamp |
| */ |
| @NonNull |
| public NetworkCapabilities getNetworkCapabilities() { |
| return new NetworkCapabilities(mNetworkCapabilities); |
| } |
| |
| /** |
| * Returns a PersistableBundle with additional info for this report. |
| * |
| * <p>Gets a bundle with details about the suspected data stall including information |
| * specific to the monitoring method that detected the data stall. |
| * |
| * @return PersistableBundle that may contain additional information on the suspected data |
| * stall |
| */ |
| @NonNull |
| public PersistableBundle getStallDetails() { |
| return new PersistableBundle(mStallDetails); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (this == o) return true; |
| if (!(o instanceof DataStallReport)) return false; |
| final DataStallReport that = (DataStallReport) o; |
| |
| // PersistableBundle is optimized to avoid unparcelling data unless fields are |
| // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over |
| // {@link PersistableBundle#kindofEquals}. |
| return mReportTimestamp == that.mReportTimestamp |
| && mDetectionMethod == that.mDetectionMethod |
| && mNetwork.equals(that.mNetwork) |
| && mLinkProperties.equals(that.mLinkProperties) |
| && mNetworkCapabilities.equals(that.mNetworkCapabilities) |
| && persistableBundleEquals(mStallDetails, that.mStallDetails); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash( |
| mNetwork, |
| mReportTimestamp, |
| mDetectionMethod, |
| mLinkProperties, |
| mNetworkCapabilities, |
| mStallDetails); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeParcelable(mNetwork, flags); |
| dest.writeLong(mReportTimestamp); |
| dest.writeInt(mDetectionMethod); |
| dest.writeParcelable(mLinkProperties, flags); |
| dest.writeParcelable(mNetworkCapabilities, flags); |
| dest.writeParcelable(mStallDetails, flags); |
| } |
| |
| /** Implement the Parcelable interface */ |
| public static final @NonNull Creator<DataStallReport> CREATOR = |
| new Creator<DataStallReport>() { |
| public DataStallReport createFromParcel(Parcel in) { |
| return new DataStallReport( |
| in.readParcelable(null), |
| in.readLong(), |
| in.readInt(), |
| in.readParcelable(null), |
| in.readParcelable(null), |
| in.readParcelable(null)); |
| } |
| |
| public DataStallReport[] newArray(int size) { |
| return new DataStallReport[size]; |
| } |
| }; |
| } |
| |
| /** @hide */ |
| @VisibleForTesting |
| public static class ConnectivityDiagnosticsBinder |
| extends IConnectivityDiagnosticsCallback.Stub { |
| @NonNull private final ConnectivityDiagnosticsCallback mCb; |
| @NonNull private final Executor mExecutor; |
| |
| /** @hide */ |
| @VisibleForTesting |
| public ConnectivityDiagnosticsBinder( |
| @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) { |
| this.mCb = cb; |
| this.mExecutor = executor; |
| } |
| |
| /** @hide */ |
| @VisibleForTesting |
| public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| mExecutor.execute(() -> { |
| mCb.onConnectivityReportAvailable(report); |
| }); |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| |
| /** @hide */ |
| @VisibleForTesting |
| public void onDataStallSuspected(@NonNull DataStallReport report) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| mExecutor.execute(() -> { |
| mCb.onDataStallSuspected(report); |
| }); |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| |
| /** @hide */ |
| @VisibleForTesting |
| public void onNetworkConnectivityReported( |
| @NonNull Network network, boolean hasConnectivity) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| mExecutor.execute(() -> { |
| mCb.onNetworkConnectivityReported(network, hasConnectivity); |
| }); |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| } |
| |
| /** |
| * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about |
| * network connectivity events. Must be extended by applications wanting notifications. |
| */ |
| public abstract static class ConnectivityDiagnosticsCallback { |
| /** |
| * Called when the platform completes a data connectivity check. This will also be invoked |
| * immediately upon registration for each network matching the request with the latest |
| * report, if a report has already been generated for that network. |
| * |
| * <p>The Network specified in the ConnectivityReport may not be active any more when this |
| * method is invoked. |
| * |
| * @param report The ConnectivityReport containing information about a connectivity check |
| */ |
| public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {} |
| |
| /** |
| * Called when the platform suspects a data stall on some Network. |
| * |
| * <p>The Network specified in the DataStallReport may not be active any more when this |
| * method is invoked. |
| * |
| * @param report The DataStallReport containing information about the suspected data stall |
| */ |
| public void onDataStallSuspected(@NonNull DataStallReport report) {} |
| |
| /** |
| * Called when any app reports connectivity to the System. |
| * |
| * @param network The Network for which connectivity has been reported |
| * @param hasConnectivity The connectivity reported to the System |
| */ |
| public void onNetworkConnectivityReported( |
| @NonNull Network network, boolean hasConnectivity) {} |
| } |
| |
| /** |
| * Registers a ConnectivityDiagnosticsCallback with the System. |
| * |
| * <p>Only apps that offer network connectivity to the user should be registering callbacks. |
| * These are the only apps whose callbacks will be invoked by the system. Apps considered to |
| * meet these conditions include: |
| * |
| * <ul> |
| * <li>Carrier apps with active subscriptions |
| * <li>Active VPNs |
| * <li>WiFi Suggesters |
| * </ul> |
| * |
| * <p>Callbacks registered by apps not meeting the above criteria will not be invoked. |
| * |
| * <p>If a registering app loses its relevant permissions, any callbacks it registered will |
| * silently stop receiving callbacks. Note that registering apps must also have location |
| * permissions to receive callbacks as some Networks may be location-bound (such as WiFi |
| * networks). |
| * |
| * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is |
| * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with |
| * multiple NetworkRequests, an IllegalArgumentException will be thrown. |
| * |
| * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the |
| * number of outstanding requests to 100 per app (identified by their UID), shared with |
| * callbacks in {@link ConnectivityManager}. Registering a callback with this method will count |
| * toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting |
| * this issue and to conserve resources, make sure to unregister the callbacks with |
| * {@link #unregisterConnectivityDiagnosticsCallback}. |
| * |
| * @param request The NetworkRequest that will be used to match with Networks for which |
| * callbacks will be fired |
| * @param e The Executor to be used for running the callback method invocations |
| * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the |
| * System |
| * @throws IllegalArgumentException if the same callback instance is registered with multiple |
| * NetworkRequests |
| * @throws RuntimeException if the app already has too many callbacks registered. |
| */ |
| public void registerConnectivityDiagnosticsCallback( |
| @NonNull NetworkRequest request, |
| @NonNull Executor e, |
| @NonNull ConnectivityDiagnosticsCallback callback) { |
| final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e); |
| if (sCallbacks.putIfAbsent(callback, binder) != null) { |
| throw new IllegalArgumentException("Callback is currently registered"); |
| } |
| |
| try { |
| mService.registerConnectivityDiagnosticsCallback( |
| binder, request, mContext.getOpPackageName()); |
| } catch (RemoteException exception) { |
| exception.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Unregisters a ConnectivityDiagnosticsCallback with the System. |
| * |
| * <p>If the given callback is not currently registered with the System, this operation will be |
| * a no-op. |
| * |
| * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System. |
| */ |
| public void unregisterConnectivityDiagnosticsCallback( |
| @NonNull ConnectivityDiagnosticsCallback callback) { |
| // unconditionally removing from sCallbacks prevents race conditions here, since remove() is |
| // atomic. |
| final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback); |
| if (binder == null) return; |
| |
| try { |
| mService.unregisterConnectivityDiagnosticsCallback(binder); |
| } catch (RemoteException exception) { |
| exception.rethrowFromSystemServer(); |
| } |
| } |
| } |