Files that are planned to be part of the connectivity module are grouped in packages/Connectivity, so they can be built separately and moved in one operation with their history into packages/modules/Connectivity. This places the files in the existing framework-connectivity-sources filegroup instead of the current framework-core-sources filegroup. Both are used the same way in framework-non-updatable-sources. Bug: 171540887 Test: m Change-Id: I62d9d91574ace6f5c4624035d190260c3126b91e
778 lines
31 KiB
Java
778 lines
31 KiB
Java
/*
|
|
* 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 com.android.internal.util.Preconditions;
|
|
|
|
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 = Preconditions.checkNotNull(context, "missing context");
|
|
mService = Preconditions.checkNotNull(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.
|
|
*
|
|
* <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();
|
|
}
|
|
}
|
|
}
|