[automerger skipped] Merge "[CS] Add an option to block sensitive network specifier" into pi-dev am: 9c70259e2b -s ours am: 7eb675f03d -s ours am: f39a6803e1 -s ours
am skip reason: Change-Id If08d312ff814bdde1147518f923199e6349503d5 with SHA-1 107ae95001 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12321778
Change-Id: I553eead2b24367b09f4888ea8a687c48cd2bf9da
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index a66fcae..8afeb30 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -15,7 +15,9 @@
*/
package android.net;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.IBinder;
@@ -23,6 +25,8 @@
import android.os.Parcelable;
import android.os.RemoteException;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
/**
* A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
* activity to indicate to the system different outcomes of captive portal sign in. This class is
@@ -60,10 +64,33 @@
@SystemApi
@TestApi
public static final int APP_RETURN_WANTED_AS_IS = 2;
+ /** Event offset of request codes from captive portal application. */
+ private static final int APP_REQUEST_BASE = 100;
+ /**
+ * Request code from the captive portal application, indicating that the network condition may
+ * have changed and the network should be re-validated.
+ * @see ICaptivePortal#appRequest(int)
+ * @see android.net.INetworkMonitor#forceReevaluation(int)
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
private final IBinder mBinder;
/** @hide */
+ @IntDef(value = {
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS,
+ MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR,
+ })
+ public @interface EventId {
+ }
+
+ /** @hide */
public CaptivePortal(@NonNull IBinder binder) {
mBinder = binder;
}
@@ -136,6 +163,20 @@
}
/**
+ * Request that the system reevaluates the captive portal status.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void reevaluateNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Log a captive portal login event.
* @param eventId one of the CAPTIVE_PORTAL_LOGIN_* constants in metrics_constants.proto.
* @param packageName captive portal application package name.
@@ -143,7 +184,7 @@
*/
@SystemApi
@TestApi
- public void logEvent(int eventId, @NonNull String packageName) {
+ public void logEvent(@EventId int eventId, @NonNull String packageName) {
try {
ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
} catch (RemoteException e) {
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
new file mode 100644
index 0000000..1357803
--- /dev/null
+++ b/core/java/android/net/CaptivePortalData.java
@@ -0,0 +1,281 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Metadata sent by captive portals, see https://www.ietf.org/id/draft-ietf-capport-api-03.txt.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class CaptivePortalData implements Parcelable {
+ private final long mRefreshTimeMillis;
+ @Nullable
+ private final Uri mUserPortalUrl;
+ @Nullable
+ private final Uri mVenueInfoUrl;
+ private final boolean mIsSessionExtendable;
+ private final long mByteLimit;
+ private final long mExpiryTimeMillis;
+ private final boolean mCaptive;
+
+ private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
+ boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+ mRefreshTimeMillis = refreshTimeMillis;
+ mUserPortalUrl = userPortalUrl;
+ mVenueInfoUrl = venueInfoUrl;
+ mIsSessionExtendable = isSessionExtendable;
+ mByteLimit = byteLimit;
+ mExpiryTimeMillis = expiryTimeMillis;
+ mCaptive = captive;
+ }
+
+ private CaptivePortalData(Parcel p) {
+ this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
+ p.readLong(), p.readLong(), p.readBoolean());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mRefreshTimeMillis);
+ dest.writeParcelable(mUserPortalUrl, 0);
+ dest.writeParcelable(mVenueInfoUrl, 0);
+ dest.writeBoolean(mIsSessionExtendable);
+ dest.writeLong(mByteLimit);
+ dest.writeLong(mExpiryTimeMillis);
+ dest.writeBoolean(mCaptive);
+ }
+
+ /**
+ * A builder to create new {@link CaptivePortalData}.
+ */
+ public static class Builder {
+ private long mRefreshTime;
+ private Uri mUserPortalUrl;
+ private Uri mVenueInfoUrl;
+ private boolean mIsSessionExtendable;
+ private long mBytesRemaining = -1;
+ private long mExpiryTime = -1;
+ private boolean mCaptive;
+
+ /**
+ * Create an empty builder.
+ */
+ public Builder() {}
+
+ /**
+ * Create a builder copying all data from existing {@link CaptivePortalData}.
+ */
+ public Builder(@Nullable CaptivePortalData data) {
+ if (data == null) return;
+ setRefreshTime(data.mRefreshTimeMillis)
+ .setUserPortalUrl(data.mUserPortalUrl)
+ .setVenueInfoUrl(data.mVenueInfoUrl)
+ .setSessionExtendable(data.mIsSessionExtendable)
+ .setBytesRemaining(data.mByteLimit)
+ .setExpiryTime(data.mExpiryTimeMillis)
+ .setCaptive(data.mCaptive);
+ }
+
+ /**
+ * Set the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+ */
+ @NonNull
+ public Builder setRefreshTime(long refreshTime) {
+ mRefreshTime = refreshTime;
+ return this;
+ }
+
+ /**
+ * Set the URL to be used for users to login to the portal, if captive.
+ */
+ @NonNull
+ public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
+ mUserPortalUrl = userPortalUrl;
+ return this;
+ }
+
+ /**
+ * Set the URL that can be used by users to view information about the network venue.
+ */
+ @NonNull
+ public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
+ mVenueInfoUrl = venueInfoUrl;
+ return this;
+ }
+
+ /**
+ * Set whether the portal supports extending a user session on the portal URL page.
+ */
+ @NonNull
+ public Builder setSessionExtendable(boolean sessionExtendable) {
+ mIsSessionExtendable = sessionExtendable;
+ return this;
+ }
+
+ /**
+ * Set the number of bytes remaining on the network before the portal closes.
+ */
+ @NonNull
+ public Builder setBytesRemaining(long bytesRemaining) {
+ mBytesRemaining = bytesRemaining;
+ return this;
+ }
+
+ /**
+ * Set the time at the session will expire, as per {@link System#currentTimeMillis()}.
+ */
+ @NonNull
+ public Builder setExpiryTime(long expiryTime) {
+ mExpiryTime = expiryTime;
+ return this;
+ }
+
+ /**
+ * Set whether the network is captive (portal closed).
+ */
+ @NonNull
+ public Builder setCaptive(boolean captive) {
+ mCaptive = captive;
+ return this;
+ }
+
+ /**
+ * Create a new {@link CaptivePortalData}.
+ */
+ @NonNull
+ public CaptivePortalData build() {
+ return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
+ mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+ }
+ }
+
+ /**
+ * Get the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+ */
+ public long getRefreshTimeMillis() {
+ return mRefreshTimeMillis;
+ }
+
+ /**
+ * Get the URL to be used for users to login to the portal, or extend their session if
+ * {@link #isSessionExtendable()} is true.
+ */
+ @Nullable
+ public Uri getUserPortalUrl() {
+ return mUserPortalUrl;
+ }
+
+ /**
+ * Get the URL that can be used by users to view information about the network venue.
+ */
+ @Nullable
+ public Uri getVenueInfoUrl() {
+ return mVenueInfoUrl;
+ }
+
+ /**
+ * Indicates whether the user portal URL can be used to extend sessions, when the user is logged
+ * in and the session has a time or byte limit.
+ */
+ public boolean isSessionExtendable() {
+ return mIsSessionExtendable;
+ }
+
+ /**
+ * Get the remaining bytes on the captive portal session, at the time {@link CaptivePortalData}
+ * was refreshed. This may be different from the limit currently enforced by the portal.
+ * @return The byte limit, or -1 if not set.
+ */
+ public long getByteLimit() {
+ return mByteLimit;
+ }
+
+ /**
+ * Get the time at the session will expire, as per {@link System#currentTimeMillis()}.
+ * @return The expiry time, or -1 if unset.
+ */
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
+ }
+
+ /**
+ * Get whether the network is captive (portal closed).
+ */
+ public boolean isCaptive() {
+ return mCaptive;
+ }
+
+ @NonNull
+ public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
+ @Override
+ public CaptivePortalData createFromParcel(Parcel source) {
+ return new CaptivePortalData(source);
+ }
+
+ @Override
+ public CaptivePortalData[] newArray(int size) {
+ return new CaptivePortalData[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
+ mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CaptivePortalData)) return false;
+ final CaptivePortalData other = (CaptivePortalData) obj;
+ return mRefreshTimeMillis == other.mRefreshTimeMillis
+ && Objects.equals(mUserPortalUrl, other.mUserPortalUrl)
+ && Objects.equals(mVenueInfoUrl, other.mVenueInfoUrl)
+ && mIsSessionExtendable == other.mIsSessionExtendable
+ && mByteLimit == other.mByteLimit
+ && mExpiryTimeMillis == other.mExpiryTimeMillis
+ && mCaptive == other.mCaptive;
+ }
+
+ @Override
+ public String toString() {
+ return "CaptivePortalData {"
+ + "refreshTime: " + mRefreshTimeMillis
+ + ", userPortalUrl: " + mUserPortalUrl
+ + ", venueInfoUrl: " + mVenueInfoUrl
+ + ", isSessionExtendable: " + mIsSessionExtendable
+ + ", byteLimit: " + mByteLimit
+ + ", expiryTime: " + mExpiryTimeMillis
+ + ", captive: " + mCaptive
+ + "}";
+ }
+}
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
new file mode 100644
index 0000000..704f31d
--- /dev/null
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -0,0 +1,768 @@
+/*
+ * 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) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onConnectivityReportAvailable(report);
+ });
+ });
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onDataStallSuspected(@NonNull DataStallReport report) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onDataStallSuspected(report);
+ });
+ });
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onNetworkConnectivityReported(
+ @NonNull Network network, boolean hasConnectivity) {
+ Binder.withCleanCallingIdentity(() -> {
+ mExecutor.execute(() -> {
+ mCb.onNetworkConnectivityReported(network, hasConnectivity);
+ });
+ });
+ }
+ }
+
+ /**
+ * 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();
+ }
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 111a8c4..a29f878 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -27,12 +27,15 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.SocketKeepalive.Callback;
+import android.net.TetheringManager.StartTetheringCallback;
+import android.net.TetheringManager.TetheringEventCallback;
+import android.net.TetheringManager.TetheringRequest;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -45,6 +48,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -52,13 +56,12 @@
import android.os.ServiceSpecificException;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Protocol;
@@ -76,6 +79,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -142,16 +146,6 @@
public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
/**
- * A temporary hack until SUPL system can get off the legacy APIS.
- * They do too many network requests and the long list of apps listening
- * and waking due to the CONNECTIVITY_ACTION broadcast makes it expensive.
- * Use this broadcast intent instead for SUPL requests.
- * @hide
- */
- public static final String CONNECTIVITY_ACTION_SUPL =
- "android.net.conn.CONNECTIVITY_CHANGE_SUPL";
-
- /**
* The device has connected to a network that has presented a captive
* portal, which is blocking Internet connectivity. The user was presented
* with a notification that network sign in is required,
@@ -364,7 +358,7 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage
public static final String ACTION_TETHER_STATE_CHANGED =
- "android.net.conn.TETHER_STATE_CHANGED";
+ TetheringManager.ACTION_TETHER_STATE_CHANGED;
/**
* @hide
@@ -372,14 +366,14 @@
* tethering and currently available for tethering.
*/
@UnsupportedAppUsage
- public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ public static final String EXTRA_AVAILABLE_TETHER = TetheringManager.EXTRA_AVAILABLE_TETHER;
/**
* @hide
* gives a String[] listing all the interfaces currently in local-only
* mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
*/
- public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray";
+ public static final String EXTRA_ACTIVE_LOCAL_ONLY = TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
/**
* @hide
@@ -387,7 +381,7 @@
* (ie, has DHCPv4 support and packets potentially forwarded/NATed)
*/
@UnsupportedAppUsage
- public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ public static final String EXTRA_ACTIVE_TETHER = TetheringManager.EXTRA_ACTIVE_TETHER;
/**
* @hide
@@ -396,7 +390,7 @@
* for any interfaces listed here.
*/
@UnsupportedAppUsage
- public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ public static final String EXTRA_ERRORED_TETHER = TetheringManager.EXTRA_ERRORED_TETHER;
/**
* Broadcast Action: The captive portal tracker has finished its test.
@@ -446,7 +440,7 @@
* @see #startTethering(int, boolean, OnStartTetheringCallback)
* @hide
*/
- public static final int TETHERING_INVALID = -1;
+ public static final int TETHERING_INVALID = TetheringManager.TETHERING_INVALID;
/**
* Wifi tethering type.
@@ -454,7 +448,7 @@
* @hide
*/
@SystemApi
- public static final int TETHERING_WIFI = 0;
+ public static final int TETHERING_WIFI = TetheringManager.TETHERING_WIFI;
/**
* USB tethering type.
@@ -462,7 +456,7 @@
* @hide
*/
@SystemApi
- public static final int TETHERING_USB = 1;
+ public static final int TETHERING_USB = TetheringManager.TETHERING_USB;
/**
* Bluetooth tethering type.
@@ -470,47 +464,56 @@
* @hide
*/
@SystemApi
- public static final int TETHERING_BLUETOOTH = 2;
+ public static final int TETHERING_BLUETOOTH = TetheringManager.TETHERING_BLUETOOTH;
+
+ /**
+ * Wifi P2p tethering type.
+ * Wifi P2p tethering is set through events automatically, and don't
+ * need to start from #startTethering(int, boolean, OnStartTetheringCallback).
+ * @hide
+ */
+ public static final int TETHERING_WIFI_P2P = TetheringManager.TETHERING_WIFI_P2P;
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
* @hide
*/
- public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ public static final String EXTRA_ADD_TETHER_TYPE = TetheringConstants.EXTRA_ADD_TETHER_TYPE;
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
* @hide
*/
- public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ public static final String EXTRA_REM_TETHER_TYPE = TetheringConstants.EXTRA_REM_TETHER_TYPE;
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
* @hide
*/
- public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ public static final String EXTRA_SET_ALARM = TetheringConstants.EXTRA_SET_ALARM;
/**
* Tells the TetherService to run a provision check now.
* @hide
*/
- public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ public static final String EXTRA_RUN_PROVISION = TetheringConstants.EXTRA_RUN_PROVISION;
/**
* Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
* which will receive provisioning results. Can be left empty.
* @hide
*/
- public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ public static final String EXTRA_PROVISION_CALLBACK =
+ TetheringConstants.EXTRA_PROVISION_CALLBACK;
/**
* The absence of a connection type.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ @SystemApi
public static final int TYPE_NONE = -1;
/**
@@ -655,7 +658,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @SystemApi
public static final int TYPE_WIFI_P2P = 13;
/**
@@ -703,6 +706,42 @@
@Deprecated
public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
+ /**
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_NONE,
+ TYPE_MOBILE,
+ TYPE_WIFI,
+ TYPE_MOBILE_MMS,
+ TYPE_MOBILE_SUPL,
+ TYPE_MOBILE_DUN,
+ TYPE_MOBILE_HIPRI,
+ TYPE_WIMAX,
+ TYPE_BLUETOOTH,
+ TYPE_DUMMY,
+ TYPE_ETHERNET,
+ TYPE_MOBILE_FOTA,
+ TYPE_MOBILE_IMS,
+ TYPE_MOBILE_CBS,
+ TYPE_WIFI_P2P,
+ TYPE_MOBILE_IA,
+ TYPE_MOBILE_EMERGENCY,
+ TYPE_PROXY,
+ TYPE_VPN,
+ TYPE_TEST
+ })
+ public @interface LegacyNetworkType {}
+
+ // Deprecated constants for return values of startUsingNetworkFeature. They used to live
+ // in com.android.internal.telephony.PhoneConstants until they were made inaccessible.
+ private static final int DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE = 0;
+ private static final int DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED = 1;
+ private static final int DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED = 3;
+
/** {@hide} */
public static final int MAX_RADIO_TYPE = TYPE_TEST;
@@ -789,6 +828,7 @@
private INetworkManagementService mNMService;
private INetworkPolicyManager mNPManager;
+ private final TetheringManager mTetheringManager;
/**
* Tests if a given integer represents a valid network type.
@@ -993,7 +1033,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
@Nullable
public Network getActiveNetworkForUid(int uid) {
return getActiveNetworkForUid(uid, false);
@@ -1122,7 +1162,7 @@
*
* {@hide}
*/
- @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
@UnsupportedAppUsage
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
return getActiveNetworkInfoForUid(uid, false);
@@ -1259,7 +1299,8 @@
@UnsupportedAppUsage
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
- return mService.getDefaultNetworkCapabilitiesForUser(userId);
+ return mService.getDefaultNetworkCapabilitiesForUser(
+ userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1341,7 +1382,7 @@
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
- return mService.getNetworkCapabilities(network);
+ return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1357,10 +1398,14 @@
* The system network validation may be using different strategies to detect captive portals,
* so this method does not necessarily return a URL used by the system. It only returns a URL
* that may be relevant for other components trying to detect captive portals.
+ *
* @hide
+ * @deprecated This API returns URL which is not guaranteed to be one of the URLs used by the
+ * system.
*/
+ @Deprecated
@SystemApi
- @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS)
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public String getCaptivePortalServerUrl() {
try {
return mService.getCaptivePortalServerUrl();
@@ -1399,7 +1444,7 @@
if (netCap == null) {
Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
feature);
- return PhoneConstants.APN_REQUEST_FAILED;
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
}
NetworkRequest request = null;
@@ -1409,9 +1454,9 @@
Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
renewRequestLocked(l);
if (l.currentNetwork != null) {
- return PhoneConstants.APN_ALREADY_ACTIVE;
+ return DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE;
} else {
- return PhoneConstants.APN_REQUEST_STARTED;
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
}
}
@@ -1419,10 +1464,10 @@
}
if (request != null) {
Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
- return PhoneConstants.APN_REQUEST_STARTED;
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
} else {
Log.d(TAG, " request Failed");
- return PhoneConstants.APN_REQUEST_FAILED;
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
}
}
@@ -1493,84 +1538,6 @@
return null;
}
- /**
- * Guess what the network request was trying to say so that the resulting
- * network is accessible via the legacy (deprecated) API such as
- * requestRouteToHost.
- *
- * This means we should try to be fairly precise about transport and
- * capability but ignore things such as networkSpecifier.
- * If the request has more than one transport or capability it doesn't
- * match the old legacy requests (they selected only single transport/capability)
- * so this function cannot map the request to a single legacy type and
- * the resulting network will not be available to the legacy APIs.
- *
- * This code is only called from the requestNetwork API (L and above).
- *
- * Setting a legacy type causes CONNECTIVITY_ACTION broadcasts, which are expensive
- * because they wake up lots of apps - see http://b/23350688 . So we currently only
- * do this for SUPL requests, which are the only ones that we know need it. If
- * omitting these broadcasts causes unacceptable app breakage, then for backwards
- * compatibility we can send them:
- *
- * if (targetSdkVersion < Build.VERSION_CODES.M) && // legacy API unsupported >= M
- * targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP)) // requestNetwork not present < L
- *
- * TODO - This should be removed when the legacy APIs are removed.
- */
- private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
- if (netCap == null) {
- return TYPE_NONE;
- }
-
- if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- return TYPE_NONE;
- }
-
- // Do this only for SUPL, until GnssLocationProvider is fixed. http://b/25876485 .
- if (!netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- // NOTE: if this causes app breakage, we should not just comment out this early return;
- // instead, we should make this early return conditional on the requesting app's target
- // SDK version, as described in the comment above.
- return TYPE_NONE;
- }
-
- String type = null;
- int result = TYPE_NONE;
-
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- type = "enableCBS";
- result = TYPE_MOBILE_CBS;
- } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- type = "enableIMS";
- result = TYPE_MOBILE_IMS;
- } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- type = "enableFOTA";
- result = TYPE_MOBILE_FOTA;
- } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- type = "enableDUN";
- result = TYPE_MOBILE_DUN;
- } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- type = "enableSUPL";
- result = TYPE_MOBILE_SUPL;
- // back out this hack for mms as they no longer need this and it's causing
- // device slowdowns - b/23350688 (note, supl still needs this)
- //} else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- // type = "enableMMS";
- // result = TYPE_MOBILE_MMS;
- } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- type = "enableHIPRI";
- result = TYPE_MOBILE_HIPRI;
- }
- if (type != null) {
- NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type);
- if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) {
- return result;
- }
- }
- return TYPE_NONE;
- }
-
private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
if (netCap == null) return TYPE_NONE;
if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
@@ -1755,6 +1722,9 @@
/** @hide */
public static class PacketKeepaliveCallback {
+ @UnsupportedAppUsage
+ public PacketKeepaliveCallback() {
+ }
/** The requested keepalive was successfully started. */
@UnsupportedAppUsage
public void onStarted() {}
@@ -2137,19 +2107,14 @@
@Deprecated
@UnsupportedAppUsage
public boolean getMobileDataEnabled() {
- IBinder b = ServiceManager.getService(Context.TELEPHONY_SERVICE);
- if (b != null) {
- try {
- ITelephony it = ITelephony.Stub.asInterface(b);
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
- boolean retVal = it.isUserDataEnabled(subId);
- Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
- + " retVal=" + retVal);
- return retVal;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (tm != null) {
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
+ boolean retVal = tm.createForSubscriptionId(subId).isDataEnabled();
+ Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+ + " retVal=" + retVal);
+ return retVal;
}
Log.d("ConnectivityManager", "getMobileDataEnabled()- remote exception retVal=false");
return false;
@@ -2251,6 +2216,7 @@
public ConnectivityManager(Context context, IConnectivityManager service) {
mContext = Preconditions.checkNotNull(context, "missing context");
mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+ mTetheringManager = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE);
sInstance = this;
}
@@ -2280,26 +2246,6 @@
.getPackageNameForUid(context, uid), true /* throwException */);
}
- /** {@hide} */
- public static final void enforceTetherChangePermission(Context context, String callingPkg) {
- Preconditions.checkNotNull(context, "Context cannot be null");
- Preconditions.checkNotNull(callingPkg, "callingPkg cannot be null");
-
- if (context.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app).length == 2) {
- // Have a provisioning app - must only let system apps (which check this app)
- // turn on tethering
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
- } else {
- int uid = Binder.getCallingUid();
- // If callingPkg's uid is not same as Binder.getCallingUid(),
- // AppOpsService throws SecurityException.
- Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPkg,
- true /* throwException */);
- }
- }
-
/**
* @deprecated - use getSystemService. This is a kludge to support static access in certain
* situations where a Context pointer is unavailable.
@@ -2330,16 +2276,14 @@
*
* @return an array of 0 or more Strings of tetherable interface names.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableIfaces() {
- try {
- return mService.getTetherableIfaces();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetherableIfaces();
}
/**
@@ -2347,16 +2291,14 @@
*
* @return an array of 0 or more String of currently tethered interface names.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetheredIfaces() {
- try {
- return mService.getTetheredIfaces();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetheredIfaces();
}
/**
@@ -2370,30 +2312,27 @@
* @return an array of 0 or more String indicating the interface names
* which failed to tether.
*
+ * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetheringErroredIfaces() {
- try {
- return mService.getTetheringErroredIfaces();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetheringErroredIfaces();
}
/**
* Get the set of tethered dhcp ranges.
*
- * @return an array of 0 or more {@code String} of tethered dhcp ranges.
+ * @deprecated This method is not supported.
+ * TODO: remove this function when all of clients are removed.
* {@hide}
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ @Deprecated
public String[] getTetheredDhcpRanges() {
- try {
- return mService.getTetheredDhcpRanges();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
}
/**
@@ -2417,18 +2356,14 @@
*
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
+ * @deprecated Use {@link TetheringManager#startTethering} instead
*
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int tether(String iface) {
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "tether caller:" + pkgName);
- return mService.tether(iface, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.tether(iface);
}
/**
@@ -2450,14 +2385,9 @@
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int untether(String iface) {
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "untether caller:" + pkgName);
- return mService.untether(iface, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.untether(iface);
}
/**
@@ -2476,29 +2406,24 @@
*
* @return a boolean - {@code true} indicating Tethering is supported.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead.
* {@hide}
*/
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
android.Manifest.permission.WRITE_SETTINGS})
public boolean isTetheringSupported() {
- String pkgName = mContext.getOpPackageName();
- try {
- return mService.isTetheringSupported(pkgName);
- } catch (SecurityException e) {
- // This API is not available to this caller, but for backward-compatibility
- // this will just return false instead of throwing.
- return false;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.isTetheringSupported();
}
/**
* Callback for use with {@link #startTethering} to find out whether tethering succeeded.
+ *
+ * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
public static abstract class OnStartTetheringCallback {
/**
* Called when tethering has been successfully started.
@@ -2515,9 +2440,12 @@
* Convenient overload for
* {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null
* handler to run on the current thread's {@link Looper}.
+ *
+ * @deprecated Use {@link TetheringManager#startTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback) {
@@ -2541,33 +2469,44 @@
* @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
* of the result of trying to tether.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ *
+ * @deprecated Use {@link TetheringManager#startTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback, Handler handler) {
Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");
- ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+ final Executor executor = new Executor() {
@Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == TETHER_ERROR_NO_ERROR) {
- callback.onTetheringStarted();
+ public void execute(Runnable command) {
+ if (handler == null) {
+ command.run();
} else {
- callback.onTetheringFailed();
+ handler.post(command);
}
}
};
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "startTethering caller:" + pkgName);
- mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);
- } catch (RemoteException e) {
- Log.e(TAG, "Exception trying to start tethering.", e);
- wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
- }
+ final StartTetheringCallback tetheringCallback = new StartTetheringCallback() {
+ @Override
+ public void onTetheringStarted() {
+ callback.onTetheringStarted();
+ }
+
+ @Override
+ public void onTetheringFailed(final int error) {
+ callback.onTetheringFailed();
+ }
+ };
+
+ final TetheringRequest request = new TetheringRequest.Builder(type)
+ .setShouldShowEntitlementUi(showProvisioningUi).build();
+
+ mTetheringManager.startTethering(request, executor, tetheringCallback);
}
/**
@@ -2578,27 +2517,26 @@
* {@link ConnectivityManager.TETHERING_WIFI},
* {@link ConnectivityManager.TETHERING_USB}, or
* {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ *
+ * @deprecated Use {@link TetheringManager#stopTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void stopTethering(int type) {
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "stopTethering caller:" + pkgName);
- mService.stopTethering(type, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mTetheringManager.stopTethering(type);
}
/**
* Callback for use with {@link registerTetheringEventCallback} to find out tethering
* upstream status.
*
- *@hide
+ * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead.
+ * @hide
*/
@SystemApi
+ @Deprecated
public abstract static class OnTetheringEventCallback {
/**
@@ -2612,7 +2550,7 @@
}
@GuardedBy("mTetheringEventCallbacks")
- private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback>
+ private final ArrayMap<OnTetheringEventCallback, TetheringEventCallback>
mTetheringEventCallbacks = new ArrayMap<>();
/**
@@ -2623,35 +2561,29 @@
*
* @param executor the executor on which callback will be invoked.
* @param callback the callback to be called when tethering has change events.
+ *
+ * @deprecated Use {@link TetheringManager#registerTetheringEventCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void registerTetheringEventCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull final OnTetheringEventCallback callback) {
Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null.");
+ final TetheringEventCallback tetherCallback =
+ new TetheringEventCallback() {
+ @Override
+ public void onUpstreamChanged(@Nullable Network network) {
+ callback.onUpstreamChanged(network);
+ }
+ };
+
synchronized (mTetheringEventCallbacks) {
- Preconditions.checkArgument(!mTetheringEventCallbacks.containsKey(callback),
- "callback was already registered.");
- ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
- @Override
- public void onUpstreamChanged(Network network) throws RemoteException {
- Binder.withCleanCallingIdentity(() ->
- executor.execute(() -> {
- callback.onUpstreamChanged(network);
- }));
- }
- };
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "registerTetheringUpstreamCallback:" + pkgName);
- mService.registerTetheringEventCallback(remoteCallback, pkgName);
- mTetheringEventCallbacks.put(callback, remoteCallback);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mTetheringEventCallbacks.put(callback, tetherCallback);
+ mTetheringManager.registerTetheringEventCallback(executor, tetherCallback);
}
}
@@ -2660,22 +2592,20 @@
* {@link #registerTetheringEventCallback}.
*
* @param callback previously registered callback.
+ *
+ * @deprecated Use {@link TetheringManager#unregisterTetheringEventCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void unregisterTetheringEventCallback(
@NonNull final OnTetheringEventCallback callback) {
+ Objects.requireNonNull(callback, "The callback must be non-null");
synchronized (mTetheringEventCallbacks) {
- ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback);
- Preconditions.checkNotNull(remoteCallback, "callback was not registered.");
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "unregisterTetheringEventCallback:" + pkgName);
- mService.unregisterTetheringEventCallback(remoteCallback, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final TetheringEventCallback tetherCallback =
+ mTetheringEventCallbacks.remove(callback);
+ mTetheringManager.unregisterTetheringEventCallback(tetherCallback);
}
}
@@ -2688,16 +2618,14 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable usb interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableUsbRegexs() {
- try {
- return mService.getTetherableUsbRegexs();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetherableUsbRegexs();
}
/**
@@ -2708,16 +2636,14 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable wifi interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableWifiRegexs() {
- try {
- return mService.getTetherableWifiRegexs();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetherableWifiRegexs();
}
/**
@@ -2728,16 +2654,15 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable bluetooth interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged(
+ *TetheringManager.TetheringInterfaceRegexps)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableBluetoothRegexs() {
- try {
- return mService.getTetherableBluetoothRegexs();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.getTetherableBluetoothRegexs();
}
/**
@@ -2754,51 +2679,115 @@
*
* @param enable a boolean - {@code true} to enable tethering
* @return error a {@code TETHER_ERROR} value indicating success or failure type
+ * @deprecated Use {@link TetheringManager#startTethering} instead
*
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int setUsbTethering(boolean enable) {
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "setUsbTethering caller:" + pkgName);
- return mService.setUsbTethering(enable, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return mTetheringManager.setUsbTethering(enable);
}
- /** {@hide} */
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_NO_ERROR = 0;
- /** {@hide} */
- public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
- /** {@hide} */
- public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
- /** {@hide} */
- public static final int TETHER_ERROR_UNSUPPORTED = 3;
- /** {@hide} */
- public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
- /** {@hide} */
- public static final int TETHER_ERROR_MASTER_ERROR = 5;
- /** {@hide} */
- public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
- /** {@hide} */
- public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
- /** {@hide} */
- public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8;
- /** {@hide} */
- public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
- /** {@hide} */
- public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
- /** {@hide} */
+ @Deprecated
+ public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNKNOWN_IFACE =
+ TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_SERVICE_UNAVAIL}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_SERVICE_UNAVAIL =
+ TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNSUPPORTED}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNSUPPORTED = TetheringManager.TETHER_ERROR_UNSUPPORTED;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNAVAIL_IFACE}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNAVAIL_IFACE =
+ TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_INTERNAL_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_MASTER_ERROR =
+ TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_TETHER_IFACE_ERROR =
+ TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNTETHER_IFACE_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR =
+ TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_FORWARDING_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_ENABLE_NAT_ERROR =
+ TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_FORWARDING_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_DISABLE_NAT_ERROR =
+ TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_IFACE_CFG_ERROR =
+ TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISIONING_FAILED}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_PROVISION_FAILED = 11;
- /** {@hide} */
- public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
- /** {@hide} */
+ @Deprecated
+ public static final int TETHER_ERROR_PROVISION_FAILED =
+ TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_DHCPSERVER_ERROR =
+ TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
+ @Deprecated
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN =
+ TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -2808,16 +2797,21 @@
* @return error The error code of the last error tethering or untethering the named
* interface
*
+ * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public int getLastTetherError(String iface) {
- try {
- return mService.getLastTetherError(iface);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ int error = mTetheringManager.getLastTetherError(iface);
+ if (error == TetheringManager.TETHER_ERROR_UNKNOWN_TYPE) {
+ // TETHER_ERROR_UNKNOWN_TYPE was introduced with TetheringManager and has never been
+ // returned by ConnectivityManager. Convert it to the legacy TETHER_ERROR_UNKNOWN_IFACE
+ // instead.
+ error = TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
}
+ return error;
}
/** @hide */
@@ -2833,9 +2827,12 @@
/**
* Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether
* entitlement succeeded.
+ *
+ * @deprecated Use {@link TetheringManager#OnTetheringEntitlementResultListener} instead.
* @hide
*/
@SystemApi
+ @Deprecated
public interface OnTetheringEntitlementResultListener {
/**
* Called to notify entitlement result.
@@ -2865,9 +2862,11 @@
* @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
* notify the caller of the result of entitlement check. The listener may be called zero
* or one time.
+ * @deprecated Use {@link TetheringManager#requestLatestTetheringEntitlementResult} instead.
* {@hide}
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void getLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
@NonNull @CallbackExecutor Executor executor,
@@ -2883,14 +2882,8 @@
}
};
- try {
- String pkgName = mContext.getOpPackageName();
- Log.i(TAG, "getLatestTetheringEntitlementResult:" + pkgName);
- mService.getLatestTetheringEntitlementResult(type, wrappedListener,
- showEntitlementUi, pkgName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mTetheringManager.requestLatestTetheringEntitlementResult(type, wrappedListener,
+ showEntitlementUi);
}
/**
@@ -2967,7 +2960,7 @@
* HTTP proxy. A {@code null} value will clear the global HTTP proxy.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void setGlobalProxy(ProxyInfo p) {
try {
mService.setGlobalProxy(p);
@@ -3112,6 +3105,7 @@
* Get the mobile provisioning url.
* {@hide}
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public String getMobileProvisioningUrl() {
try {
return mService.getMobileProvisioningUrl();
@@ -3144,6 +3138,7 @@
* @hide
*/
@RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_AIRPLANE_MODE,
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK})
@@ -3158,6 +3153,9 @@
/** {@hide} - returns the factory serial number */
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public int registerNetworkFactory(Messenger messenger, String name) {
try {
return mService.registerNetworkFactory(messenger, name);
@@ -3168,6 +3166,9 @@
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void unregisterNetworkFactory(Messenger messenger) {
try {
mService.unregisterNetworkFactory(messenger);
@@ -3176,6 +3177,67 @@
}
}
+ /**
+ * Registers the specified {@link NetworkProvider}.
+ * Each listener must only be registered once. The listener can be unregistered with
+ * {@link #unregisterNetworkProvider}.
+ *
+ * @param provider the provider to register
+ * @return the ID of the provider. This ID must be used by the provider when registering
+ * {@link android.net.NetworkAgent}s.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public int registerNetworkProvider(@NonNull NetworkProvider provider) {
+ if (provider.getProviderId() != NetworkProvider.ID_NONE) {
+ throw new IllegalStateException("NetworkProviders can only be registered once");
+ }
+
+ try {
+ int providerId = mService.registerNetworkProvider(provider.getMessenger(),
+ provider.getName());
+ provider.setProviderId(providerId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return provider.getProviderId();
+ }
+
+ /**
+ * Unregisters the specified NetworkProvider.
+ *
+ * @param provider the provider to unregister
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public void unregisterNetworkProvider(@NonNull NetworkProvider provider) {
+ try {
+ mService.unregisterNetworkProvider(provider.getMessenger());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ provider.setProviderId(NetworkProvider.ID_NONE);
+ }
+
+
+ /** @hide exposed via the NetworkProvider class. */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
+ try {
+ mService.declareNetworkRequestUnfulfillable(request);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
// TODO : remove this method. It is a stopgap measure to help sheperding a number
// of dependent changes that would conflict throughout the automerger graph. Having this
// temporarily helps with the process of going through with all these dependent changes across
@@ -3183,24 +3245,28 @@
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkMisc misc) {
- return registerNetworkAgent(messenger, ni, lp, nc, score, misc,
- NetworkFactory.SerialNumber.NONE);
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+ return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
}
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) {
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
try {
- return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc,
- factorySerialNumber);
+ return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3265,42 +3331,77 @@
/**
* Called when the framework connects and has declared a new network ready for use.
- * This callback may be called more than once if the {@link Network} that is
- * satisfying the request changes. This will always immediately be followed by a
- * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
- * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
- * {@link #onBlockedStatusChanged(Network, boolean)}.
+ *
+ * <p>For callbacks registered with {@link #registerNetworkCallback}, multiple networks may
+ * be available at the same time, and onAvailable will be called for each of these as they
+ * appear.
+ *
+ * <p>For callbacks registered with {@link #requestNetwork} and
+ * {@link #registerDefaultNetworkCallback}, this means the network passed as an argument
+ * is the new best network for this request and is now tracked by this callback ; this
+ * callback will no longer receive method calls about other networks that may have been
+ * passed to this method previously. The previously-best network may have disconnected, or
+ * it may still be around and the newly-best network may simply be better.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O}, this will always immediately
+ * be followed by a call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}
+ * then by a call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call
+ * to {@link #onBlockedStatusChanged(Network, boolean)}.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions (there is no guarantee the objects
+ * returned by these methods will be current). Instead, wait for a call to
+ * {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link #onLinkPropertiesChanged(Network, LinkProperties)} whose arguments are guaranteed
+ * to be well-ordered with respect to other callbacks.
*
* @param network The {@link Network} of the satisfying network.
*/
public void onAvailable(@NonNull Network network) {}
/**
- * Called when the network is about to be disconnected. Often paired with an
- * {@link NetworkCallback#onAvailable} call with the new replacement network
- * for graceful handover. This may not be called if we have a hard loss
- * (loss without warning). This may be followed by either a
- * {@link NetworkCallback#onLost} call or a
- * {@link NetworkCallback#onAvailable} call for this network depending
- * on whether we lose or regain it.
+ * Called when the network is about to be lost, typically because there are no outstanding
+ * requests left for it. This may be paired with a {@link NetworkCallback#onAvailable} call
+ * with the new replacement network for graceful handover. This method is not guaranteed
+ * to be called before {@link NetworkCallback#onLost} is called, for example in case a
+ * network is suddenly disconnected.
*
- * @param network The {@link Network} that is about to be disconnected.
- * @param maxMsToLive The time in ms the framework will attempt to keep the
- * network connected. Note that the network may suffer a
- * hard loss at any time.
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions ; calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
+ * @param network The {@link Network} that is about to be lost.
+ * @param maxMsToLive The time in milliseconds the system intends to keep the network
+ * connected for graceful handover; note that the network may still
+ * suffer a hard loss at any time.
*/
public void onLosing(@NonNull Network network, int maxMsToLive) {}
/**
- * Called when the framework has a hard loss of the network or when the
- * graceful failure ends.
+ * Called when a network disconnects or otherwise no longer satisfies this request or
+ * callback.
+ *
+ * <p>If the callback was registered with requestNetwork() or
+ * registerDefaultNetworkCallback(), it will only be invoked against the last network
+ * returned by onAvailable() when that network is lost and no other network satisfies
+ * the criteria of the request.
+ *
+ * <p>If the callback was registered with registerNetworkCallback() it will be called for
+ * each network which no longer satisfies the criteria of the callback.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions ; calling these methods while in a
+ * callback may return an outdated or even a null object.
*
* @param network The {@link Network} lost.
*/
public void onLost(@NonNull Network network) {}
/**
- * Called if no network is found in the timeout time specified in
+ * Called if no network is found within the timeout time specified in
* {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call or if the
* requested network request cannot be fulfilled (whether or not a timeout was
* specified). When this callback is invoked the associated
@@ -3310,8 +3411,15 @@
public void onUnavailable() {}
/**
- * Called when the network the framework connected to for this request
- * changes capabilities but still satisfies the stated need.
+ * Called when the network corresponding to this request changes capabilities but still
+ * satisfies the requested criteria.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+ * to be called immediately after {@link #onAvailable}.
+ *
+ * <p>Do NOT call {@link #getLinkProperties(Network)} or other synchronous
+ * ConnectivityManager methods in this callback as this is prone to race conditions :
+ * calling these methods while in a callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose capabilities have changed.
* @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
@@ -3321,8 +3429,14 @@
@NonNull NetworkCapabilities networkCapabilities) {}
/**
- * Called when the network the framework connected to for this request
- * changes {@link LinkProperties}.
+ * Called when the network corresponding to this request changes {@link LinkProperties}.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+ * to be called immediately after {@link #onAvailable}.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or other synchronous
+ * ConnectivityManager methods in this callback as this is prone to race conditions :
+ * calling these methods while in a callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose link properties have changed.
* @param linkProperties The new {@link LinkProperties} for this network.
@@ -3331,12 +3445,20 @@
@NonNull LinkProperties linkProperties) {}
/**
- * Called when the network the framework connected to for this request
- * goes into {@link NetworkInfo.State#SUSPENDED}.
- * This generally means that while the TCP connections are still live,
- * temporarily network data fails to transfer. Specifically this is used
- * on cellular networks to mask temporary outages when driving through
- * a tunnel, etc.
+ * Called when the network the framework connected to for this request suspends data
+ * transmission temporarily.
+ *
+ * <p>This generally means that while the TCP connections are still live temporarily
+ * network data fails to transfer. To give a specific example, this is used on cellular
+ * networks to mask temporary outages when driving through a tunnel, etc. In general this
+ * means read operations on sockets on this network will block once the buffers are
+ * drained, and write operations will block once the buffers are full.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions (there is no guarantee the objects
+ * returned by these methods will be current).
+ *
* @hide
*/
public void onNetworkSuspended(@NonNull Network network) {}
@@ -3345,6 +3467,12 @@
* Called when the network the framework connected to for this request
* returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
* preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
+
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions : calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
* @hide
*/
public void onNetworkResumed(@NonNull Network network) {}
@@ -3352,6 +3480,11 @@
/**
* Called when access to the specified network is blocked or unblocked.
*
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions : calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
* @param network The {@link Network} whose blocked status has changed.
* @param blocked The blocked status of this {@link Network}.
*/
@@ -3537,6 +3670,7 @@
checkCallbackNotNull(callback);
Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
final NetworkRequest request;
+ final String callingPackageName = mContext.getOpPackageName();
try {
synchronized(sCallbacks) {
if (callback.networkRequest != null
@@ -3548,10 +3682,11 @@
Messenger messenger = new Messenger(handler);
Binder binder = new Binder();
if (action == LISTEN) {
- request = mService.listenForNetwork(need, messenger, binder);
+ request = mService.listenForNetwork(
+ need, messenger, binder, callingPackageName);
} else {
request = mService.requestNetwork(
- need, messenger, timeoutMs, binder, legacyType);
+ need, messenger, timeoutMs, binder, legacyType, callingPackageName);
}
if (request != null) {
sCallbacks.put(request, callback);
@@ -3569,17 +3704,29 @@
/**
* Helper function to request a network with a particular legacy type.
*
- * This is temporarily public @hide so it can be called by system code that uses the
- * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for
- * instead network notifications.
+ * This API is only for use in internal system code that requests networks with legacy type and
+ * relies on CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead.
*
- * TODO: update said system code to rely on NetworkCallbacks and make this method private.
+ * @param request {@link NetworkRequest} describing this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
+ * @param legacyType to specify the network type(#TYPE_*).
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
public void requestNetwork(@NonNull NetworkRequest request,
- @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
- @NonNull Handler handler) {
+ int timeoutMs, int legacyType, @NonNull Handler handler,
+ @NonNull NetworkCallback networkCallback) {
+ if (legacyType == TYPE_NONE) {
+ throw new IllegalArgumentException("TYPE_NONE is meaningless legacy type");
+ }
CallbackHandler cbHandler = new CallbackHandler(handler);
NetworkCapabilities nc = request.networkCapabilities;
sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3588,50 +3735,51 @@
/**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
- * This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
- * version of the method which takes a timeout is
- * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
- * Status of the request can be followed by listening to the various
- * callbacks described in {@link NetworkCallback}. The {@link Network}
- * can be used to direct traffic to the network.
- * <p>It is presently unsupported to request a network with mutable
- * {@link NetworkCapabilities} such as
- * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
- * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
- * as these {@code NetworkCapabilities} represent states that a particular
- * network may never attain, and whether a network will attain these states
- * is unknown prior to bringing up the network so the framework does not
- * know how to go about satisfing a request with these capabilities.
+ * <p>This method will attempt to find the best network that matches the passed
+ * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
+ * criteria. The platform will evaluate which network is the best at its own discretion.
+ * Throughput, latency, cost per byte, policy, user preference and other considerations
+ * may be factored in the decision of what is considered the best network.
*
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>As long as this request is outstanding, the platform will try to maintain the best network
+ * matching this request, while always attempting to match the request to a better network if
+ * possible. If a better match is found, the platform will switch this request to the now-best
+ * network and inform the app of the newly best network by invoking
+ * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform
+ * will not try to maintain any other network than the best one currently matching the request:
+ * a network not matching any network request may be disconnected at any time.
*
- * @param request {@link NetworkRequest} describing this request.
- * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
- * the callback must not be shared - it uniquely specifies this request.
- * The callback is invoked on the default internal Handler.
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
- */
- public void requestNetwork(@NonNull NetworkRequest request,
- @NonNull NetworkCallback networkCallback) {
- requestNetwork(request, networkCallback, getDefaultHandler());
- }
-
- /**
- * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ * <p>For example, an application could use this method to obtain a connected cellular network
+ * even if the device currently has a data connection over Ethernet. This may cause the cellular
+ * radio to consume additional power. Or, an application could inform the system that it wants
+ * a network supporting sending MMSes and have the system let it know about the currently best
+ * MMS-supporting network through the provided {@link NetworkCallback}.
*
- * This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
- * version of the method which takes a timeout is
- * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
- * Status of the request can be followed by listening to the various
- * callbacks described in {@link NetworkCallback}. The {@link Network}
- * can be used to direct traffic to the network.
+ * <p>The status of the request can be followed by listening to the various callbacks described
+ * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be
+ * used to direct traffic to the network (although accessing some networks may be subject to
+ * holding specific permissions). Callers will learn about the specific characteristics of the
+ * network through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the
+ * provided {@link NetworkCallback} will only be invoked due to changes in the best network
+ * matching the request at any given time; therefore when a better network matching the request
+ * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called
+ * with the new network after which no further updates are given about the previously-best
+ * network, unless it becomes the best again at some later time. All callbacks are invoked
+ * in order on the same thread, which by default is a thread created by the framework running
+ * in the app.
+ * {@see #requestNetwork(NetworkRequest, NetworkCallback, Handler)} to change where the
+ * callbacks are invoked.
+ *
+ * <p>This{@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at
+ * which point the system may let go of the network at any time.
+ *
+ * <p>A version of this method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}, that an app can use to only
+ * wait for a limited amount of time for the network to become unavailable.
+ *
* <p>It is presently unsupported to request a network with mutable
* {@link NetworkCapabilities} such as
* {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
@@ -3646,19 +3794,48 @@
* or the ability to modify system settings as determined by
* {@link android.provider.Settings.System#canWrite}.</p>
*
+ * <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
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * The callback is invoked on the default internal Handler.
+ * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ */
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback) {
+ requestNetwork(request, networkCallback, getDefaultHandler());
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ *
+ * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
+ * but runs all the callbacks on the passed Handler.
+ *
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
* the callback must not be shared - it uniquely specifies this request.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
- int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
CallbackHandler cbHandler = new CallbackHandler(handler);
- requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
}
/**
@@ -3677,10 +3854,9 @@
* timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
* for that purpose. Calling this method will attempt to bring up the requested network.
*
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3688,36 +3864,26 @@
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable()} is called. The timeout must
* be a positive value (i.e. >0).
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, int timeoutMs) {
checkTimeout(timeoutMs);
- int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
- requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE,
+ getDefaultHandler());
}
/**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
- * This function behaves identically to the version without timeout, but if a suitable
- * network is not found within the given time (in milliseconds) the
- * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
- * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
- * not have to be released if timed-out (it is automatically released). Unregistering a
- * request that timed out is not an error.
+ * This method behaves identically to
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} but runs all the callbacks
+ * on the passed Handler.
*
- * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
- * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
- * for that purpose. Calling this method will attempt to bring up the requested network.
- *
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, is subject to the same limitations,
+ * and throws the same exceptions in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3725,16 +3891,13 @@
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable} is called.
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
checkTimeout(timeoutMs);
- int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
CallbackHandler cbHandler = new CallbackHandler(handler);
- requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE, cbHandler);
}
/**
@@ -3789,6 +3952,15 @@
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfying a request with these capabilities.
*
+ * <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
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(PendingIntent)}
+ * or {@link #releaseNetworkRequest(PendingIntent)}.
+ *
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
* or the ability to modify system settings as determined by
@@ -3800,14 +3972,15 @@
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
* @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
* @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
try {
- mService.pendingRequestForNetwork(request.networkCapabilities, operation);
+ mService.pendingRequestForNetwork(
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -3855,10 +4028,20 @@
* either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
* called.
*
+ * <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
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
* networks change state.
* The callback is invoked on the default internal Handler.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -3872,10 +4055,21 @@
* either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
* called.
*
+ * <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
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
* networks change state.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -3909,10 +4103,21 @@
* <p>
* The request may be released normally by calling
* {@link #unregisterNetworkCallback(android.app.PendingIntent)}.
+ *
+ * <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
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(PendingIntent)}
+ * or {@link #releaseNetworkRequest(PendingIntent)}.
+ *
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerNetworkCallback(@NonNull NetworkRequest request,
@@ -3920,7 +4125,8 @@
printStackTrace();
checkPendingIntentNotNull(operation);
try {
- mService.pendingListenForNetwork(request.networkCapabilities, operation);
+ mService.pendingListenForNetwork(
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -3933,9 +4139,19 @@
* will continue to be called until either the application exits or
* {@link #unregisterNetworkCallback(NetworkCallback)} is called.
*
+ * <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
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
* The callback is invoked on the default internal Handler.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
@@ -3947,9 +4163,19 @@
* will continue to be called until either the application exits or
* {@link #unregisterNetworkCallback(NetworkCallback)} is called.
*
+ * <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
+ * all variants of this method, of {@link #requestNetwork} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network 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 #unregisterNetworkCallback(NetworkCallback)}.
+ *
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws RuntimeException if the app already has too many callbacks registered.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
@@ -4041,7 +4267,6 @@
* Cannot be null.
*/
public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
- checkPendingIntentNotNull(operation);
releaseNetworkRequest(operation);
}
@@ -4117,7 +4342,7 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void startCaptivePortalApp(Network network) {
try {
mService.startCaptivePortalApp(network);
@@ -4233,9 +4458,11 @@
* Resets all connectivity manager settings back to factory defaults.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void factoryReset() {
try {
mService.factoryReset();
+ mTetheringManager.stopAllTethering();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4481,19 +4708,19 @@
/**
* Returns the {@code uid} of the owner of a network connection.
*
- * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
- * {@code IPPROTO_UDP} currently supported.
+ * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and {@code
+ * IPPROTO_UDP} currently supported.
* @param local The local {@link InetSocketAddress} of a connection.
* @param remote The remote {@link InetSocketAddress} of a connection.
- *
* @return {@code uid} if the connection is found and the app has permission to observe it
- * (e.g., if it is associated with the calling VPN app's tunnel) or
- * {@link android.os.Process#INVALID_UID} if the connection is not found.
- * Throws {@link SecurityException} if the caller is not the active VPN for the current user.
- * Throws {@link IllegalArgumentException} if an unsupported protocol is requested.
+ * (e.g., if it is associated with the calling VPN app's VpnService tunnel) or {@link
+ * android.os.Process#INVALID_UID} if the connection is not found.
+ * @throws {@link SecurityException} if the caller is not the active VpnService for the current
+ * user.
+ * @throws {@link IllegalArgumentException} if an unsupported protocol is requested.
*/
- public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
- @NonNull InetSocketAddress remote) {
+ public int getConnectionOwnerUid(
+ int protocol, @NonNull InetSocketAddress local, @NonNull InetSocketAddress remote) {
ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
try {
return mService.getConnectionOwnerUid(connectionInfo);
@@ -4516,4 +4743,28 @@
Log.d(TAG, "StackLog:" + sb.toString());
}
}
+
+ /**
+ * Simulates a Data Stall for the specified Network.
+ *
+ * <p>The caller must be the owner of the specified Network.
+ *
+ * @param detectionMethod The detection method used to identify the Data Stall.
+ * @param timestampMillis The timestamp at which the stall 'occurred', in milliseconds.
+ * @param network The Network for which a Data Stall is being simluated.
+ * @param extras The PersistableBundle of extras included in the Data Stall notification.
+ * @throws SecurityException if the caller is not the owner of the given network.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_STACK})
+ public void simulateDataStall(int detectionMethod, long timestampMillis,
+ @NonNull Network network, @NonNull PersistableBundle extras) {
+ try {
+ mService.simulateDataStall(detectionMethod, timestampMillis, network, extras);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 98bab44..912df67 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -16,8 +16,8 @@
package android.net;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
/**
* A simple object for retrieving the results of a DHCP request.
@@ -67,12 +67,12 @@
buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress());
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ipAddress);
dest.writeInt(gateway);
@@ -83,7 +83,7 @@
dest.writeInt(leaseDuration);
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public static final @android.annotation.NonNull Creator<DhcpInfo> CREATOR =
new Creator<DhcpInfo>() {
public DhcpInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 0b1a845..3f7660f 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -38,6 +38,8 @@
import android.system.ErrnoException;
import android.util.Log;
+import com.android.net.module.util.DnsPacket;
+
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -97,7 +99,7 @@
@interface DnsError {}
/**
* Indicates that there was an error parsing the response the query.
- * The cause of this error is available via getCause() and is a ParseException.
+ * The cause of this error is available via getCause() and is a {@link ParseException}.
*/
public static final int ERROR_PARSE = 0;
/**
@@ -290,8 +292,15 @@
}
try {
mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
- } catch (ParseException e) {
- mDnsException = new DnsException(ERROR_PARSE, e);
+ } catch (DnsPacket.ParseException e) {
+ // Convert the com.android.net.module.util.DnsPacket.ParseException to an
+ // android.net.ParseException. This is the type that was used in Q and is implied
+ // by the public documentation of ERROR_PARSE.
+ //
+ // DnsPacket cannot throw android.net.ParseException directly because it's @hide.
+ ParseException pe = new ParseException(e.reason, e.getCause());
+ pe.setStackTrace(e.getStackTrace());
+ mDnsException = new DnsException(ERROR_PARSE, pe);
}
maybeReportAnswer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 61648dc..69a47f24 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,12 +18,13 @@
import android.app.PendingIntent;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.LinkProperties;
-import android.net.ITetheringEventCallback;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
@@ -33,6 +34,7 @@
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
import com.android.internal.net.LegacyVpnInfo;
@@ -52,13 +54,15 @@
@UnsupportedAppUsage
NetworkInfo getActiveNetworkInfo();
NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked);
+ @UnsupportedAppUsage(maxTargetSdk = 28)
NetworkInfo getNetworkInfo(int networkType);
NetworkInfo getNetworkInfoForUid(in Network network, int uid, boolean ignoreBlocked);
@UnsupportedAppUsage
NetworkInfo[] getAllNetworkInfo();
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
- NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
+ NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName);
boolean isNetworkSupported(int networkType);
@@ -67,7 +71,7 @@
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
- NetworkCapabilities getNetworkCapabilities(in Network network);
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
@UnsupportedAppUsage
NetworkState[] getAllNetworkState();
@@ -77,41 +81,32 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
- int tether(String iface, String callerPkg);
-
- int untether(String iface, String callerPkg);
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getLastTetherError} as alternative")
int getLastTetherError(String iface);
- boolean isTetheringSupported(String callerPkg);
-
- void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg);
-
- void stopTethering(int type, String callerPkg);
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableIfaces} as alternative")
String[] getTetherableIfaces();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetheredIfaces} as alternative")
String[] getTetheredIfaces();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetheringErroredIfaces} "
+ + "as Alternative")
String[] getTetheringErroredIfaces();
- String[] getTetheredDhcpRanges();
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableUsbRegexs} as alternative")
String[] getTetherableUsbRegexs();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableWifiRegexs} as alternative")
String[] getTetherableWifiRegexs();
- String[] getTetherableBluetoothRegexs();
-
- int setUsbTethering(boolean enable, String callerPkg);
-
+ @UnsupportedAppUsage(maxTargetSdk = 28)
void reportInetCondition(int networkType, int percentage);
void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
@@ -124,10 +119,18 @@
boolean prepareVpn(String oldPackage, String newPackage, int userId);
- void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
+ void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
ParcelFileDescriptor establishVpn(in VpnConfig config);
+ boolean provisionVpnProfile(in VpnProfile profile, String packageName);
+
+ void deleteVpnProfile(String packageName);
+
+ void startVpnProfile(String packageName);
+
+ void stopVpnProfile(String packageName);
+
VpnConfig getVpnConfig(int userId);
@UnsupportedAppUsage
@@ -151,28 +154,34 @@
void setAirplaneMode(boolean enable);
- int registerNetworkFactory(in Messenger messenger, in String name);
-
boolean requestBandwidthUpdate(in Network network);
+ int registerNetworkFactory(in Messenger messenger, in String name);
void unregisterNetworkFactory(in Messenger messenger);
- int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
- in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
+ int registerNetworkProvider(in Messenger messenger, in String name);
+ void unregisterNetworkProvider(in Messenger messenger);
+
+ void declareNetworkRequestUnfulfillable(in NetworkRequest request);
+
+ Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+ in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
+ in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
+ String callingPackageName);
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation);
+ in PendingIntent operation, String callingPackageName);
void releasePendingNetworkRequest(in PendingIntent operation);
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, in IBinder binder);
+ in Messenger messenger, in IBinder binder, String callingPackageName);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation);
+ in PendingIntent operation, String callingPackageName);
void releaseNetworkRequest(in NetworkRequest networkRequest);
@@ -215,11 +224,12 @@
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
- void getLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
- boolean showEntitlementUi, String callerPkg);
-
- void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
- void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
+ void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
+ in NetworkRequest request, String callingPackageName);
+ void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
IBinder startOrGetTestNetworkService();
+
+ void simulateDataStall(int detectionMethod, long timestampMillis, in Network network,
+ in PersistableBundle extras);
}
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
new file mode 100644
index 0000000..1873d77
--- /dev/null
+++ b/core/java/android/net/InvalidPacketException.java
@@ -0,0 +1,66 @@
+/*
+ * 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.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Thrown when a packet is invalid.
+ * @hide
+ */
+@SystemApi
+public final class InvalidPacketException extends Exception {
+ private final int mError;
+
+ // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
+ /** Invalid IP address. */
+ public static final int ERROR_INVALID_IP_ADDRESS = -21;
+
+ // Must match SocketKeepalive#ERROR_INVALID_PORT.
+ /** Invalid port number. */
+ public static final int ERROR_INVALID_PORT = -22;
+
+ // Must match SocketKeepalive#ERROR_INVALID_LENGTH.
+ /** Invalid packet length. */
+ public static final int ERROR_INVALID_LENGTH = -23;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_INVALID_IP_ADDRESS,
+ ERROR_INVALID_PORT,
+ ERROR_INVALID_LENGTH
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * This packet is invalid.
+ * See the error code for details.
+ */
+ public InvalidPacketException(@ErrorCode final int error) {
+ this.mError = error;
+ }
+
+ /** Get error code. */
+ public int getError() {
+ return mError;
+ }
+}
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 2af82d7..23d5ff7 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -16,8 +16,11 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
-import android.net.StaticIpConfiguration;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,13 +30,17 @@
* A class representing a configured network.
* @hide
*/
-public class IpConfiguration implements Parcelable {
+@SystemApi
+public final class IpConfiguration implements Parcelable {
private static final String TAG = "IpConfiguration";
+ // This enum has been used by apps through reflection for many releases.
+ // Therefore they can't just be removed. Duplicating these constants to
+ // give an alternate SystemApi is a worse option than exposing them.
+ @SuppressLint("Enum")
public enum IpAssignment {
/* Use statically configured IP settings. Configuration can be accessed
* with staticIpConfiguration */
- @UnsupportedAppUsage
STATIC,
/* Use dynamically configured IP settings */
DHCP,
@@ -42,14 +49,19 @@
UNASSIGNED
}
+ /** @hide */
public IpAssignment ipAssignment;
+ /** @hide */
public StaticIpConfiguration staticIpConfiguration;
+ // This enum has been used by apps through reflection for many releases.
+ // Therefore they can't just be removed. Duplicating these constants to
+ // give an alternate SystemApi is a worse option than exposing them.
+ @SuppressLint("Enum")
public enum ProxySettings {
/* No proxy is to be used. Any existing proxy settings
* should be cleared. */
- @UnsupportedAppUsage
NONE,
/* Use statically configured proxy. Configuration can be accessed
* with httpProxy. */
@@ -62,8 +74,10 @@
PAC
}
+ /** @hide */
public ProxySettings proxySettings;
+ /** @hide */
@UnsupportedAppUsage
public ProxyInfo httpProxy;
@@ -83,6 +97,7 @@
init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null);
}
+ /** @hide */
@UnsupportedAppUsage
public IpConfiguration(IpAssignment ipAssignment,
ProxySettings proxySettings,
@@ -91,7 +106,7 @@
init(ipAssignment, proxySettings, staticIpConfiguration, httpProxy);
}
- public IpConfiguration(IpConfiguration source) {
+ public IpConfiguration(@NonNull IpConfiguration source) {
this();
if (source != null) {
init(source.ipAssignment, source.proxySettings,
@@ -99,35 +114,35 @@
}
}
- public IpAssignment getIpAssignment() {
+ public @NonNull IpAssignment getIpAssignment() {
return ipAssignment;
}
- public void setIpAssignment(IpAssignment ipAssignment) {
+ public void setIpAssignment(@NonNull IpAssignment ipAssignment) {
this.ipAssignment = ipAssignment;
}
- public StaticIpConfiguration getStaticIpConfiguration() {
+ public @Nullable StaticIpConfiguration getStaticIpConfiguration() {
return staticIpConfiguration;
}
- public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) {
+ public void setStaticIpConfiguration(@Nullable StaticIpConfiguration staticIpConfiguration) {
this.staticIpConfiguration = staticIpConfiguration;
}
- public ProxySettings getProxySettings() {
+ public @NonNull ProxySettings getProxySettings() {
return proxySettings;
}
- public void setProxySettings(ProxySettings proxySettings) {
+ public void setProxySettings(@NonNull ProxySettings proxySettings) {
this.proxySettings = proxySettings;
}
- public ProxyInfo getHttpProxy() {
+ public @Nullable ProxyInfo getHttpProxy() {
return httpProxy;
}
- public void setHttpProxy(ProxyInfo httpProxy) {
+ public void setHttpProxy(@Nullable ProxyInfo httpProxy) {
this.httpProxy = httpProxy;
}
@@ -180,8 +195,8 @@
return 0;
}
- /** Implement the Parcelable interface */
- public void writeToParcel(Parcel dest, int flags) {
+ /** Implement the Parcelable interface */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(ipAssignment.name());
dest.writeString(proxySettings.name());
dest.writeParcelable(staticIpConfiguration, flags);
@@ -189,7 +204,7 @@
}
/** Implement the Parcelable interface */
- public static final @android.annotation.NonNull Creator<IpConfiguration> CREATOR =
+ public static final @NonNull Creator<IpConfiguration> CREATOR =
new Creator<IpConfiguration>() {
public IpConfiguration createFromParcel(Parcel in) {
IpConfiguration config = new IpConfiguration();
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 9b8b732..e21cb44 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -16,13 +16,13 @@
package android.net;
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
-import android.net.SocketKeepalive.InvalidPacketException;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.net.util.IpUtils;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Log;
import java.net.InetAddress;
@@ -30,38 +30,45 @@
/**
* Represents the actual packets that are sent by the
* {@link android.net.SocketKeepalive} API.
- *
* @hide
*/
-public class KeepalivePacketData implements Parcelable {
+@SystemApi
+public class KeepalivePacketData {
private static final String TAG = "KeepalivePacketData";
/** Source IP address */
- public final InetAddress srcAddress;
+ @NonNull
+ private final InetAddress mSrcAddress;
/** Destination IP address */
- public final InetAddress dstAddress;
+ @NonNull
+ private final InetAddress mDstAddress;
/** Source port */
- public final int srcPort;
+ private final int mSrcPort;
/** Destination port */
- public final int dstPort;
+ private final int mDstPort;
/** Packet data. A raw byte string of packet data, not including the link-layer header. */
private final byte[] mPacket;
- protected static final int IPV4_HEADER_LENGTH = 20;
- protected static final int UDP_HEADER_LENGTH = 8;
+ // Note: If you add new fields, please modify the parcelling code in the child classes.
+
// This should only be constructed via static factory methods, such as
- // nattKeepalivePacket
- protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
- InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
- this.srcAddress = srcAddress;
- this.dstAddress = dstAddress;
- this.srcPort = srcPort;
- this.dstPort = dstPort;
+ // nattKeepalivePacket.
+ /**
+ * A holding class for data necessary to build a keepalive packet.
+ */
+ protected KeepalivePacketData(@NonNull InetAddress srcAddress,
+ @IntRange(from = 0, to = 65535) int srcPort, @NonNull InetAddress dstAddress,
+ @IntRange(from = 0, to = 65535) int dstPort,
+ @NonNull byte[] data) throws InvalidPacketException {
+ this.mSrcAddress = srcAddress;
+ this.mDstAddress = dstAddress;
+ this.mSrcPort = srcPort;
+ this.mDstPort = dstPort;
this.mPacket = data;
// Check we have two IP addresses of the same family.
@@ -78,42 +85,34 @@
}
}
+ /** Get source IP address. */
+ @NonNull
+ public InetAddress getSrcAddress() {
+ return mSrcAddress;
+ }
+
+ /** Get destination IP address. */
+ @NonNull
+ public InetAddress getDstAddress() {
+ return mDstAddress;
+ }
+
+ /** Get source port number. */
+ public int getSrcPort() {
+ return mSrcPort;
+ }
+
+ /** Get destination port number. */
+ public int getDstPort() {
+ return mDstPort;
+ }
+
+ /**
+ * Returns a byte array of the given packet data.
+ */
+ @NonNull
public byte[] getPacket() {
return mPacket.clone();
}
- /* Parcelable Implementation */
- public int describeContents() {
- return 0;
- }
-
- /** Write to parcel */
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(srcAddress.getHostAddress());
- out.writeString(dstAddress.getHostAddress());
- out.writeInt(srcPort);
- out.writeInt(dstPort);
- out.writeByteArray(mPacket);
- }
-
- protected KeepalivePacketData(Parcel in) {
- srcAddress = NetworkUtils.numericToInetAddress(in.readString());
- dstAddress = NetworkUtils.numericToInetAddress(in.readString());
- srcPort = in.readInt();
- dstPort = in.readInt();
- mPacket = in.createByteArray();
- }
-
- /** Parcelable Creator */
- public static final @android.annotation.NonNull Parcelable.Creator<KeepalivePacketData> CREATOR =
- new Parcelable.Creator<KeepalivePacketData>() {
- public KeepalivePacketData createFromParcel(Parcel in) {
- return new KeepalivePacketData(in);
- }
-
- public KeepalivePacketData[] newArray(int size) {
- return new KeepalivePacketData[size];
- }
- };
-
}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 93dd2e4..a9d7f17 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -19,6 +19,7 @@
import static android.system.OsConstants.IFA_F_DADFAILED;
import static android.system.OsConstants.IFA_F_DEPRECATED;
import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_PERMANENT;
import static android.system.OsConstants.IFA_F_TENTATIVE;
import static android.system.OsConstants.RT_SCOPE_HOST;
import static android.system.OsConstants.RT_SCOPE_LINK;
@@ -30,10 +31,11 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.Pair;
import java.net.Inet4Address;
@@ -41,6 +43,7 @@
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
+import java.util.Objects;
/**
* Identifies an IP address on a network link.
@@ -58,6 +61,21 @@
* </ul>
*/
public class LinkAddress implements Parcelable {
+
+ /**
+ * Indicates the deprecation or expiration time is unknown
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_UNKNOWN = -1;
+
+ /**
+ * Indicates this address is permanent.
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_PERMANENT = Long.MAX_VALUE;
+
/**
* IPv4 or IPv6 address.
*/
@@ -71,7 +89,9 @@
private int prefixLength;
/**
- * Address flags. A bitmask of IFA_F_* values.
+ * Address flags. A bitmask of {@code IFA_F_*} values. Note that {@link #getFlags()} may not
+ * return these exact values. For example, it may set or clear the {@code IFA_F_DEPRECATED}
+ * flag depending on the current preferred lifetime.
*/
private int flags;
@@ -81,6 +101,23 @@
private int scope;
/**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this LinkAddress will be
+ * or was deprecated. At the time existing connections can still use this address until it
+ * expires, but new connections should use the new address. {@link #LIFETIME_UNKNOWN} indicates
+ * this information is not available. {@link #LIFETIME_PERMANENT} indicates this
+ * {@link LinkAddress} will never be deprecated.
+ */
+ private long deprecationTime;
+
+ /**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this {@link LinkAddress}
+ * will expire and be removed from the interface. {@link #LIFETIME_UNKNOWN} indicates this
+ * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress}
+ * will never expire.
+ */
+ private long expirationTime;
+
+ /**
* Utility function to determines the scope of a unicast address. Per RFC 4291 section 2.5 and
* RFC 6724 section 3.2.
* @hide
@@ -152,7 +189,8 @@
/**
* Utility function for the constructors.
*/
- private void init(InetAddress address, int prefixLength, int flags, int scope) {
+ private void init(InetAddress address, int prefixLength, int flags, int scope,
+ long deprecationTime, long expirationTime) {
if (address == null ||
address.isMulticastAddress() ||
prefixLength < 0 ||
@@ -161,15 +199,42 @@
throw new IllegalArgumentException("Bad LinkAddress params " + address +
"/" + prefixLength);
}
+
+ // deprecation time and expiration time must be both provided, or neither.
+ if ((deprecationTime == LIFETIME_UNKNOWN) != (expirationTime == LIFETIME_UNKNOWN)) {
+ throw new IllegalArgumentException(
+ "Must not specify only one of deprecation time and expiration time");
+ }
+
+ // deprecation time needs to be a positive value.
+ if (deprecationTime != LIFETIME_UNKNOWN && deprecationTime < 0) {
+ throw new IllegalArgumentException("invalid deprecation time " + deprecationTime);
+ }
+
+ // expiration time needs to be a positive value.
+ if (expirationTime != LIFETIME_UNKNOWN && expirationTime < 0) {
+ throw new IllegalArgumentException("invalid expiration time " + expirationTime);
+ }
+
+ // expiration time can't be earlier than deprecation time
+ if (deprecationTime != LIFETIME_UNKNOWN && expirationTime != LIFETIME_UNKNOWN
+ && expirationTime < deprecationTime) {
+ throw new IllegalArgumentException("expiration earlier than deprecation ("
+ + deprecationTime + ", " + expirationTime + ")");
+ }
+
this.address = address;
this.prefixLength = prefixLength;
this.flags = flags;
this.scope = scope;
+ this.deprecationTime = deprecationTime;
+ this.expirationTime = expirationTime;
}
/**
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with
* the specified flags and scope. Flags and scope are not checked for validity.
+ *
* @param address The IP address.
* @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
@@ -181,7 +246,39 @@
@TestApi
public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
int flags, int scope) {
- init(address, prefixLength, flags, scope);
+ init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from an {@code InetAddress}, prefix length, with
+ * the specified flags, scope, deprecation time, and expiration time. Flags and scope are not
+ * checked for validity. The value of the {@code IFA_F_DEPRECATED} and {@code IFA_F_PERMANENT}
+ * flag will be adjusted based on the passed-in lifetimes.
+ *
+ * @param address The IP address.
+ * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
+ * @param scope An integer defining the scope in which the address is unique (e.g.,
+ * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
+ * @param deprecationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when
+ * this {@link LinkAddress} will be or was deprecated. At the time
+ * existing connections can still use this address until it expires, but
+ * new connections should use the new address. {@link #LIFETIME_UNKNOWN}
+ * indicates this information is not available.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never be deprecated.
+ * @param expirationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when this
+ * {@link LinkAddress} will expire and be removed from the interface.
+ * {@link #LIFETIME_UNKNOWN} indicates this information is not available.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never expire.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int scope, long deprecationTime, long expirationTime) {
+ init(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -237,7 +334,7 @@
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
// TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
- init(ipAndMask.first, ipAndMask.second, flags, scope);
+ init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
}
/**
@@ -265,10 +362,12 @@
return false;
}
LinkAddress linkAddress = (LinkAddress) obj;
- return this.address.equals(linkAddress.address) &&
- this.prefixLength == linkAddress.prefixLength &&
- this.flags == linkAddress.flags &&
- this.scope == linkAddress.scope;
+ return this.address.equals(linkAddress.address)
+ && this.prefixLength == linkAddress.prefixLength
+ && this.flags == linkAddress.flags
+ && this.scope == linkAddress.scope
+ && this.deprecationTime == linkAddress.deprecationTime
+ && this.expirationTime == linkAddress.expirationTime;
}
/**
@@ -276,7 +375,7 @@
*/
@Override
public int hashCode() {
- return address.hashCode() + 11 * prefixLength + 19 * flags + 43 * scope;
+ return Objects.hash(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -329,6 +428,25 @@
* Returns the flags of this {@code LinkAddress}.
*/
public int getFlags() {
+ int flags = this.flags;
+ if (deprecationTime != LIFETIME_UNKNOWN) {
+ if (SystemClock.elapsedRealtime() >= deprecationTime) {
+ flags |= IFA_F_DEPRECATED;
+ } else {
+ // If deprecation time is in the future, or permanent.
+ flags &= ~IFA_F_DEPRECATED;
+ }
+ }
+
+ if (expirationTime == LIFETIME_PERMANENT) {
+ flags |= IFA_F_PERMANENT;
+ } else if (expirationTime != LIFETIME_UNKNOWN) {
+ // If we know this address expired or will expire in the future, then this address
+ // should not be permanent.
+ flags &= ~IFA_F_PERMANENT;
+ }
+
+ // Do no touch the original flags. Return the adjusted flags here.
return flags;
}
@@ -340,7 +458,42 @@
}
/**
- * Returns true if this {@code LinkAddress} is global scope and preferred.
+ * Get the deprecation time, as reported by {@link SystemClock#elapsedRealtime}, when this
+ * {@link LinkAddress} will be or was deprecated. At the time existing connections can still use
+ * this address until it expires, but new connections should use the new address.
+ *
+ * @return The deprecation time in milliseconds. {@link #LIFETIME_UNKNOWN} indicates this
+ * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress}
+ * will never be deprecated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getDeprecationTime() {
+ return deprecationTime;
+ }
+
+ /**
+ * Get the expiration time, as reported by {@link SystemClock#elapsedRealtime}, when this
+ * {@link LinkAddress} will expire and be removed from the interface.
+ *
+ * @return The expiration time in milliseconds. {@link #LIFETIME_UNKNOWN} indicates this
+ * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress}
+ * will never expire.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+
+ /**
+ * Returns true if this {@code LinkAddress} is global scope and preferred (i.e., not currently
+ * deprecated).
+ *
* @hide
*/
@TestApi
@@ -352,6 +505,7 @@
* state has cleared either DAD has succeeded or failed, and both
* flags are cleared regardless).
*/
+ int flags = getFlags();
return (scope == RT_SCOPE_UNIVERSE
&& !isIpv6ULA()
&& (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
@@ -373,6 +527,8 @@
dest.writeInt(prefixLength);
dest.writeInt(this.flags);
dest.writeInt(scope);
+ dest.writeLong(deprecationTime);
+ dest.writeLong(expirationTime);
}
/**
@@ -392,7 +548,10 @@
int prefixLength = in.readInt();
int flags = in.readInt();
int scope = in.readInt();
- return new LinkAddress(address, prefixLength, flags, scope);
+ long deprecationTime = in.readLong();
+ long expirationTime = in.readLong();
+ return new LinkAddress(address, prefixLength, flags, scope, deprecationTime,
+ expirationTime);
}
public LinkAddress[] newArray(int size) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 94d4eb9..651494d 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -20,7 +20,9 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.LinkPropertiesUtils;
+import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -63,14 +65,25 @@
private String mPrivateDnsServerName;
private String mDomains;
private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
+ private Inet4Address mDhcpServerAddress;
private ProxyInfo mHttpProxy;
private int mMtu;
// in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
private String mTcpBufferSizes;
private IpPrefix mNat64Prefix;
+ private boolean mWakeOnLanSupported;
+ private Uri mCaptivePortalApiUrl;
+ private CaptivePortalData mCaptivePortalData;
+
+ /**
+ * Indicates whether parceling should preserve fields that are set based on permissions of
+ * the process receiving the {@link LinkProperties}.
+ */
+ private final transient boolean mParcelSensitiveFields;
private static final int MIN_MTU = 68;
- private static final int MIN_MTU_V6 = 1280;
+ /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
+ static final int MIN_MTU_V6 = 1280;
private static final int MAX_MTU = 10000;
private static final int INET6_ADDR_LENGTH = 16;
@@ -82,36 +95,8 @@
/**
* @hide
*/
- public static class CompareResult<T> {
- public final List<T> removed = new ArrayList<>();
- public final List<T> added = new ArrayList<>();
-
- public CompareResult() {}
-
- public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
- if (oldItems != null) {
- removed.addAll(oldItems);
- }
- if (newItems != null) {
- for (T newItem : newItems) {
- if (!removed.remove(newItem)) {
- added.add(newItem);
- }
- }
- }
- }
-
- @Override
- public String toString() {
- return "removed=[" + TextUtils.join(",", removed)
- + "] added=[" + TextUtils.join(",", added)
- + "]";
- }
- }
-
- /**
- * @hide
- */
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Landroid/net/LinkProperties$ProvisioningChange;")
public enum ProvisioningChange {
@UnsupportedAppUsage
STILL_NOT_PROVISIONED,
@@ -170,6 +155,7 @@
* Constructs a new {@code LinkProperties} with default values.
*/
public LinkProperties() {
+ mParcelSensitiveFields = false;
}
/**
@@ -178,24 +164,44 @@
@SystemApi
@TestApi
public LinkProperties(@Nullable LinkProperties source) {
- if (source != null) {
- mIfaceName = source.mIfaceName;
- mLinkAddresses.addAll(source.mLinkAddresses);
- mDnses.addAll(source.mDnses);
- mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
- mUsePrivateDns = source.mUsePrivateDns;
- mPrivateDnsServerName = source.mPrivateDnsServerName;
- mPcscfs.addAll(source.mPcscfs);
- mDomains = source.mDomains;
- mRoutes.addAll(source.mRoutes);
- mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
- for (LinkProperties l: source.mStackedLinks.values()) {
- addStackedLink(l);
- }
- setMtu(source.mMtu);
- mTcpBufferSizes = source.mTcpBufferSizes;
- mNat64Prefix = source.mNat64Prefix;
+ this(source, false /* parcelSensitiveFields */);
+ }
+
+ /**
+ * Create a copy of a {@link LinkProperties} that may preserve fields that were set
+ * based on the permissions of the process that originally received it.
+ *
+ * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+ * they should not be shared outside of the process that receives them without appropriate
+ * checks.
+ * @param parcelSensitiveFields Whether the sensitive fields should be kept when parceling
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+ mParcelSensitiveFields = parcelSensitiveFields;
+ if (source == null) return;
+ mIfaceName = source.mIfaceName;
+ mLinkAddresses.addAll(source.mLinkAddresses);
+ mDnses.addAll(source.mDnses);
+ mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
+ mUsePrivateDns = source.mUsePrivateDns;
+ mPrivateDnsServerName = source.mPrivateDnsServerName;
+ mPcscfs.addAll(source.mPcscfs);
+ mDomains = source.mDomains;
+ mRoutes.addAll(source.mRoutes);
+ mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
+ for (LinkProperties l: source.mStackedLinks.values()) {
+ addStackedLink(l);
}
+ setMtu(source.mMtu);
+ setDhcpServerAddress(source.getDhcpServerAddress());
+ mTcpBufferSizes = source.mTcpBufferSizes;
+ mNat64Prefix = source.mNat64Prefix;
+ mWakeOnLanSupported = source.mWakeOnLanSupported;
+ mCaptivePortalApiUrl = source.mCaptivePortalApiUrl;
+ mCaptivePortalData = source.mCaptivePortalData;
}
/**
@@ -225,7 +231,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public @NonNull List<String> getAllInterfaceNames() {
List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
if (mIfaceName != null) interfaceNames.add(mIfaceName);
@@ -245,7 +251,7 @@
* @return An unmodifiable {@link List} of {@link InetAddress} for this link.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public @NonNull List<InetAddress> getAddresses() {
final List<InetAddress> addresses = new ArrayList<>();
for (LinkAddress linkAddress : mLinkAddresses) {
@@ -340,8 +346,8 @@
* Returns all the addresses on this link and all the links stacked above it.
* @hide
*/
- @UnsupportedAppUsage
- public List<LinkAddress> getAllLinkAddresses() {
+ @SystemApi
+ public @NonNull List<LinkAddress> getAllLinkAddresses() {
List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
for (LinkProperties stacked: mStackedLinks.values()) {
addresses.addAll(stacked.getAllLinkAddresses());
@@ -456,6 +462,24 @@
}
/**
+ * Set DHCP server address.
+ *
+ * @param serverAddress the server address to set.
+ */
+ public void setDhcpServerAddress(@Nullable Inet4Address serverAddress) {
+ mDhcpServerAddress = serverAddress;
+ }
+
+ /**
+ * Get DHCP server address
+ *
+ * @return The current DHCP server address.
+ */
+ public @Nullable Inet4Address getDhcpServerAddress() {
+ return mDhcpServerAddress;
+ }
+
+ /**
* Returns the private DNS server name that is in use. If not {@code null},
* private DNS is in strict mode. In this mode, applications should ensure
* that all DNS queries are encrypted and sent to this hostname and that
@@ -540,6 +564,7 @@
* @return true if the PCSCF server was added, false otherwise.
* @hide
*/
+ @SystemApi
public boolean addPcscfServer(@NonNull InetAddress pcscfServer) {
if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) {
mPcscfs.add(pcscfServer);
@@ -661,17 +686,29 @@
route.getDestination(),
route.getGateway(),
mIfaceName,
- route.getType());
+ route.getType(),
+ route.getMtu());
+ }
+
+ private int findRouteIndexByRouteKey(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).getRouteKey().equals(route.getRouteKey())) {
+ return i;
+ }
+ }
+ return -1;
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
- * {@link RouteInfo} had an interface name set and that differs from the interface set for this
- * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
- * course is to add either un-named or properly named {@link RouteInfo}.
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
+ * with the same {@link RouteInfo.RouteKey} with different properties
+ * (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an
+ * interface name set and that differs from the interface set for this
+ * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.
+ * The proper course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
- * @return {@code false} if the route was already present, {@code true} if it was added.
+ * @return {@code true} was added or updated, false otherwise.
*/
public boolean addRoute(@NonNull RouteInfo route) {
String routeIface = route.getInterface();
@@ -681,11 +718,20 @@
+ " vs. " + mIfaceName);
}
route = routeWithInterface(route);
- if (!mRoutes.contains(route)) {
+
+ int i = findRouteIndexByRouteKey(route);
+ if (i == -1) {
+ // Route was not present. Add it.
mRoutes.add(route);
return true;
+ } else if (mRoutes.get(i).equals(route)) {
+ // Route was present and has same properties. Do nothing.
+ return false;
+ } else {
+ // Route was present and has different properties. Update it.
+ mRoutes.set(i, route);
+ return true;
}
- return false;
}
/**
@@ -693,6 +739,7 @@
* specify an interface and the interface must match the interface of this
* {@code LinkProperties}, or it will not be removed.
*
+ * @param route A {@link RouteInfo} specifying the route to remove.
* @return {@code true} if the route was removed, {@code false} if it was not present.
*
* @hide
@@ -727,7 +774,7 @@
* Returns all the routes on this link and all the links stacked above it.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public @NonNull List<RouteInfo> getAllRoutes() {
List<RouteInfo> routes = new ArrayList<>(mRoutes);
for (LinkProperties stacked: mStackedLinks.values()) {
@@ -760,10 +807,7 @@
* Returns the NAT64 prefix in use on this link, if any.
*
* @return the NAT64 prefix or {@code null}.
- * @hide
*/
- @SystemApi
- @TestApi
public @Nullable IpPrefix getNat64Prefix() {
return mNat64Prefix;
}
@@ -775,10 +819,7 @@
* 128-bit IPv6 address) are supported or {@code null} for no prefix.
*
* @param prefix the NAT64 prefix.
- * @hide
*/
- @SystemApi
- @TestApi
public void setNat64Prefix(@Nullable IpPrefix prefix) {
if (prefix != null && prefix.getPrefixLength() != 96) {
throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
@@ -841,6 +882,11 @@
* Clears this object to its initial state.
*/
public void clear() {
+ if (mParcelSensitiveFields) {
+ throw new UnsupportedOperationException(
+ "Cannot clear LinkProperties when parcelSensitiveFields is set");
+ }
+
mIfaceName = null;
mLinkAddresses.clear();
mDnses.clear();
@@ -852,8 +898,12 @@
mHttpProxy = null;
mStackedLinks.clear();
mMtu = 0;
+ mDhcpServerAddress = null;
mTcpBufferSizes = null;
mNat64Prefix = null;
+ mWakeOnLanSupported = false;
+ mCaptivePortalApiUrl = null;
+ mCaptivePortalData = null;
}
/**
@@ -915,6 +965,23 @@
resultJoiner.add("MTU:");
resultJoiner.add(Integer.toString(mMtu));
+ if (mWakeOnLanSupported) {
+ resultJoiner.add("WakeOnLanSupported: true");
+ }
+
+ if (mDhcpServerAddress != null) {
+ resultJoiner.add("ServerAddress:");
+ resultJoiner.add(mDhcpServerAddress.toString());
+ }
+
+ if (mCaptivePortalApiUrl != null) {
+ resultJoiner.add("CaptivePortalApiUrl: " + mCaptivePortalApiUrl);
+ }
+
+ if (mCaptivePortalData != null) {
+ resultJoiner.add("CaptivePortalData: " + mCaptivePortalData);
+ }
+
if (mTcpBufferSizes != null) {
resultJoiner.add("TcpBufferSizes:");
resultJoiner.add(mTcpBufferSizes);
@@ -1007,6 +1074,21 @@
}
/**
+ * Returns true if this link has an IPv4 unreachable default route.
+ *
+ * @return {@code true} if there is an IPv4 unreachable default route, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIpv4UnreachableDefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv4UnreachableDefault()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* For backward compatibility.
* This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
* just yet.
@@ -1024,7 +1106,7 @@
* @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public boolean hasIpv4DefaultRoute() {
for (RouteInfo r : mRoutes) {
if (r.isIPv4Default()) {
@@ -1035,6 +1117,21 @@
}
/**
+ * Returns true if this link has an IPv6 unreachable default route.
+ *
+ * @return {@code true} if there is an IPv6 unreachable default route, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIpv6UnreachableDefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv6UnreachableDefault()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* For backward compatibility.
* This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
* just yet.
@@ -1081,7 +1178,7 @@
* @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public boolean hasIpv4DnsServer() {
for (InetAddress ia : mDnses) {
if (ia instanceof Inet4Address) {
@@ -1109,7 +1206,7 @@
* @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public boolean hasIpv6DnsServer() {
for (InetAddress ia : mDnses) {
if (ia instanceof Inet6Address) {
@@ -1265,7 +1362,18 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
- return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
+ return LinkPropertiesUtils.isIdenticalInterfaceName(target, this);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} DHCP server address against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalDhcpServerAddress(@NonNull LinkProperties target) {
+ return Objects.equals(mDhcpServerAddress, target.mDhcpServerAddress);
}
/**
@@ -1277,10 +1385,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
- Collection<InetAddress> targetAddresses = target.getAddresses();
- Collection<InetAddress> sourceAddresses = getAddresses();
- return (sourceAddresses.size() == targetAddresses.size()) ?
- sourceAddresses.containsAll(targetAddresses) : false;
+ return LinkPropertiesUtils.isIdenticalAddresses(target, this);
}
/**
@@ -1292,15 +1397,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalDnses(@NonNull LinkProperties target) {
- Collection<InetAddress> targetDnses = target.getDnsServers();
- String targetDomains = target.getDomains();
- if (mDomains == null) {
- if (targetDomains != null) return false;
- } else {
- if (!mDomains.equals(targetDomains)) return false;
- }
- return (mDnses.size() == targetDnses.size()) ?
- mDnses.containsAll(targetDnses) : false;
+ return LinkPropertiesUtils.isIdenticalDnses(target, this);
}
/**
@@ -1353,9 +1450,7 @@
*/
@UnsupportedAppUsage
public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
- Collection<RouteInfo> targetRoutes = target.getRoutes();
- return (mRoutes.size() == targetRoutes.size()) ?
- mRoutes.containsAll(targetRoutes) : false;
+ return LinkPropertiesUtils.isIdenticalRoutes(target, this);
}
/**
@@ -1367,8 +1462,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
- return getHttpProxy() == null ? target.getHttpProxy() == null :
- getHttpProxy().equals(target.getHttpProxy());
+ return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
}
/**
@@ -1427,6 +1521,110 @@
}
/**
+ * Compares this {@code LinkProperties} WakeOnLan supported against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalWakeOnLan(LinkProperties target) {
+ return isWakeOnLanSupported() == target.isWakeOnLanSupported();
+ }
+
+ /**
+ * Compares this {@code LinkProperties}'s CaptivePortalApiUrl against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalCaptivePortalApiUrl(LinkProperties target) {
+ return Objects.equals(mCaptivePortalApiUrl, target.mCaptivePortalApiUrl);
+ }
+
+ /**
+ * Compares this {@code LinkProperties}'s CaptivePortalData against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalCaptivePortalData(LinkProperties target) {
+ return Objects.equals(mCaptivePortalData, target.mCaptivePortalData);
+ }
+
+ /**
+ * Set whether the network interface supports WakeOnLAN
+ *
+ * @param supported WakeOnLAN supported value
+ *
+ * @hide
+ */
+ public void setWakeOnLanSupported(boolean supported) {
+ mWakeOnLanSupported = supported;
+ }
+
+ /**
+ * Returns whether the network interface supports WakeOnLAN
+ *
+ * @return {@code true} if interface supports WakeOnLAN, {@code false} otherwise.
+ */
+ public boolean isWakeOnLanSupported() {
+ return mWakeOnLanSupported;
+ }
+
+ /**
+ * Set the URL of the captive portal API endpoint to get more information about the network.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void setCaptivePortalApiUrl(@Nullable Uri url) {
+ mCaptivePortalApiUrl = url;
+ }
+
+ /**
+ * Get the URL of the captive portal API endpoint to get more information about the network.
+ *
+ * <p>This is null unless the application has
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+ * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions, and the network provided
+ * the URL.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Nullable
+ public Uri getCaptivePortalApiUrl() {
+ return mCaptivePortalApiUrl;
+ }
+
+ /**
+ * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void setCaptivePortalData(@Nullable CaptivePortalData data) {
+ mCaptivePortalData = data;
+ }
+
+ /**
+ * Get the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+ *
+ * <p>This is null unless the application has
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+ * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Nullable
+ public CaptivePortalData getCaptivePortalData() {
+ return mCaptivePortalData;
+ }
+
+ /**
* Compares this {@code LinkProperties} instance against the target
* LinkProperties in {@code obj}. Two LinkPropertieses are equal if
* all their fields are equal in values.
@@ -1454,6 +1652,7 @@
*/
return isIdenticalInterfaceName(target)
&& isIdenticalAddresses(target)
+ && isIdenticalDhcpServerAddress(target)
&& isIdenticalDnses(target)
&& isIdenticalPrivateDns(target)
&& isIdenticalValidatedPrivateDnses(target)
@@ -1463,27 +1662,10 @@
&& isIdenticalStackedLinks(target)
&& isIdenticalMtu(target)
&& isIdenticalTcpBufferSizes(target)
- && isIdenticalNat64Prefix(target);
- }
-
- /**
- * Compares the addresses in this LinkProperties with another
- * LinkProperties, examining only addresses on the base link.
- *
- * @param target a LinkProperties with the new list of addresses
- * @return the differences between the addresses.
- * @hide
- */
- public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) {
- /*
- * Duplicate the LinkAddresses into removed, we will be removing
- * address which are common between mLinkAddresses and target
- * leaving the addresses that are different. And address which
- * are in target but not in mLinkAddresses are placed in the
- * addedAddresses.
- */
- return new CompareResult<>(mLinkAddresses,
- target != null ? target.getLinkAddresses() : null);
+ && isIdenticalNat64Prefix(target)
+ && isIdenticalWakeOnLan(target)
+ && isIdenticalCaptivePortalApiUrl(target)
+ && isIdenticalCaptivePortalData(target);
}
/**
@@ -1577,9 +1759,12 @@
+ mMtu * 51
+ ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ (mUsePrivateDns ? 57 : 0)
+ + ((null == mDhcpServerAddress) ? 0 : mDhcpServerAddress.hashCode())
+ mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
- + Objects.hash(mNat64Prefix);
+ + Objects.hash(mNat64Prefix)
+ + (mWakeOnLanSupported ? 71 : 0)
+ + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData);
}
/**
@@ -1598,6 +1783,7 @@
dest.writeString(mPrivateDnsServerName);
writeAddresses(dest, mPcscfs);
dest.writeString(mDomains);
+ writeAddress(dest, mDhcpServerAddress);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
dest.writeInt(mRoutes.size());
@@ -1615,6 +1801,10 @@
ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
dest.writeList(stackedLinks);
+
+ dest.writeBoolean(mWakeOnLanSupported);
+ dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalApiUrl : null, 0);
+ dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalData : null, 0);
}
private static void writeAddresses(@NonNull Parcel dest, @NonNull List<InetAddress> list) {
@@ -1624,8 +1814,9 @@
}
}
- private static void writeAddress(@NonNull Parcel dest, @NonNull InetAddress addr) {
- dest.writeByteArray(addr.getAddress());
+ private static void writeAddress(@NonNull Parcel dest, @Nullable InetAddress addr) {
+ byte[] addressBytes = (addr == null ? null : addr.getAddress());
+ dest.writeByteArray(addressBytes);
if (addr instanceof Inet6Address) {
final Inet6Address v6Addr = (Inet6Address) addr;
final boolean hasScopeId = v6Addr.getScopeId() != 0;
@@ -1634,9 +1825,11 @@
}
}
- @NonNull
+ @Nullable
private static InetAddress readAddress(@NonNull Parcel p) throws UnknownHostException {
final byte[] addr = p.createByteArray();
+ if (addr == null) return null;
+
if (addr.length == INET6_ADDR_LENGTH) {
final boolean hasScopeId = p.readBoolean();
final int scopeId = hasScopeId ? p.readInt() : 0;
@@ -1683,6 +1876,10 @@
} catch (UnknownHostException e) { }
}
netProp.setDomains(in.readString());
+ try {
+ netProp.setDhcpServerAddress((Inet4Address) InetAddress
+ .getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) { }
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
addressCount = in.readInt();
@@ -1698,6 +1895,10 @@
for (LinkProperties stackedLink: stackedLinks) {
netProp.addStackedLink(stackedLink);
}
+ netProp.setWakeOnLanSupported(in.readBoolean());
+
+ netProp.setCaptivePortalApiUrl(in.readParcelable(null));
+ netProp.setCaptivePortalData(in.readParcelable(null));
return netProp;
}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index aa8e010..0eb3c1e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -19,11 +19,12 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.MacAddressUtils;
+import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -32,13 +33,14 @@
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.Arrays;
-import java.util.Random;
/**
* Representation of a MAC address.
*
* This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
*/
public final class MacAddress implements Parcelable {
@@ -84,6 +86,9 @@
private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");
+ /** Default wifi MAC address used for a special purpose **/
+ private static final MacAddress DEFAULT_MAC_ADDRESS =
+ MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
// Internal representation of the MAC address as a single 8 byte long.
// The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
@@ -105,21 +110,13 @@
if (equals(BROADCAST_ADDRESS)) {
return TYPE_BROADCAST;
}
- if (isMulticastAddress()) {
+ if ((mAddr & MULTICAST_MASK) != 0) {
return TYPE_MULTICAST;
}
return TYPE_UNICAST;
}
/**
- * @return true if this MacAddress is a multicast address.
- * @hide
- */
- public boolean isMulticastAddress() {
- return (mAddr & MULTICAST_MASK) != 0;
- }
-
- /**
* @return true if this MacAddress is a locally assigned address.
*/
public boolean isLocallyAssigned() {
@@ -127,12 +124,22 @@
}
/**
+ * Convert this MacAddress to a byte array.
+ *
+ * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+ * the returned array is [1, 2, 3, 4, 5, 6].
+ *
* @return a byte array representation of this MacAddress.
*/
public @NonNull byte[] toByteArray() {
return byteAddrFromLongAddr(mAddr);
}
+ /**
+ * Returns a human-readable representation of this MacAddress.
+ * The exact format is implementation-dependent and should not be assumed to have any
+ * particular format.
+ */
@Override
public @NonNull String toString() {
return stringAddrFromLongAddr(mAddr);
@@ -188,7 +195,7 @@
* @hide
*/
public static boolean isMacAddress(byte[] addr) {
- return addr != null && addr.length == ETHER_ADDR_LEN;
+ return MacAddressUtils.isMacAddress(addr);
}
/**
@@ -257,26 +264,11 @@
}
private static byte[] byteAddrFromLongAddr(long addr) {
- byte[] bytes = new byte[ETHER_ADDR_LEN];
- int index = ETHER_ADDR_LEN;
- while (index-- > 0) {
- bytes[index] = (byte) addr;
- addr = addr >> 8;
- }
- return bytes;
+ return MacAddressUtils.byteAddrFromLongAddr(addr);
}
private static long longAddrFromByteAddr(byte[] addr) {
- Preconditions.checkNotNull(addr);
- if (!isMacAddress(addr)) {
- throw new IllegalArgumentException(
- Arrays.toString(addr) + " was not a valid MAC address");
- }
- long longAddr = 0;
- for (byte b : addr) {
- longAddr = (longAddr << 8) + BitUtils.uint8(b);
- }
- return longAddr;
+ return MacAddressUtils.longAddrFromByteAddr(addr);
}
// Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
@@ -346,44 +338,7 @@
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
- return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
- }
-
- /**
- * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
- * unicast bit, are randomly selected.
- *
- * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
- *
- * @return a random locally assigned, unicast MacAddress.
- *
- * @hide
- */
- public static @NonNull MacAddress createRandomUnicastAddress() {
- SecureRandom r = new SecureRandom();
- long addr = r.nextLong() & VALID_LONG_MASK;
- addr |= LOCALLY_ASSIGNED_MASK;
- addr &= ~MULTICAST_MASK;
- return new MacAddress(addr);
- }
-
- /**
- * Returns a randomly generated MAC address using the given Random object and the same
- * OUI values as the given MacAddress.
- *
- * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
- *
- * @param base a base MacAddress whose OUI is used for generating the random address.
- * @param r a standard Java Random object used for generating the random address.
- * @return a random locally assigned MacAddress.
- *
- * @hide
- */
- public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
- long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
- addr |= LOCALLY_ASSIGNED_MASK;
- addr &= ~MULTICAST_MASK;
- return new MacAddress(addr);
+ return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
}
// Convenience function for working around the lack of byte literals.
@@ -406,7 +361,6 @@
* @param mask MacAddress representing the mask to use during comparison.
* @return true if this MAC Address matches the given range.
*
- * @hide
*/
public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
Preconditions.checkNotNull(baseAddress);
@@ -420,7 +374,6 @@
* IPv6 address per RFC 4862.
*
* @return A link-local Inet6Address constructed from the MAC address.
- * @hide
*/
public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() {
byte[] macEui48Bytes = toByteArray();
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index a77c244..22288b6 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -16,10 +16,12 @@
package android.net;
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
-import android.net.SocketKeepalive.InvalidPacketException;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.util.IpUtils;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,19 +31,25 @@
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Objects;
/** @hide */
+@SystemApi
public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int UDP_HEADER_LENGTH = 8;
+
// This should only be constructed via static factory methods, such as
// nattKeepalivePacket
- private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
- InetAddress dstAddress, int dstPort, byte[] data) throws
+ public NattKeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
+ @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data) throws
InvalidPacketException {
super(srcAddress, srcPort, dstAddress, dstPort, data);
}
/**
* Factory method to create Nat-T keepalive packet structure.
+ * @hide
*/
public static NattKeepalivePacketData nattKeepalivePacket(
InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
@@ -85,15 +93,15 @@
}
/** Write to parcel */
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(srcAddress.getHostAddress());
- out.writeString(dstAddress.getHostAddress());
- out.writeInt(srcPort);
- out.writeInt(dstPort);
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(getSrcAddress().getHostAddress());
+ out.writeString(getDstAddress().getHostAddress());
+ out.writeInt(getSrcPort());
+ out.writeInt(getDstPort());
}
/** Parcelable Creator */
- public static final Parcelable.Creator<NattKeepalivePacketData> CREATOR =
+ public static final @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR =
new Parcelable.Creator<NattKeepalivePacketData>() {
public NattKeepalivePacketData createFromParcel(Parcel in) {
final InetAddress srcAddress =
@@ -107,7 +115,7 @@
dstAddress, dstPort);
} catch (InvalidPacketException e) {
throw new IllegalArgumentException(
- "Invalid NAT-T keepalive data: " + e.error);
+ "Invalid NAT-T keepalive data: " + e.getError());
}
}
@@ -115,4 +123,21 @@
return new NattKeepalivePacketData[size];
}
};
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof NattKeepalivePacketData)) return false;
+ final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
+ final InetAddress srcAddress = getSrcAddress();
+ final InetAddress dstAddress = getDstAddress();
+ return srcAddress.equals(other.getSrcAddress())
+ && dstAddress.equals(other.getDstAddress())
+ && getSrcPort() == other.getSrcPort()
+ && getDstPort() == other.getDstPort();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort());
+ }
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3f56def..f807a49 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.system.ErrnoException;
@@ -61,6 +61,7 @@
public class Network implements Parcelable {
/**
+ * The unique id of the network.
* @hide
*/
@UnsupportedAppUsage
@@ -169,6 +170,17 @@
}
/**
+ * Get the unique id of the network.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public int getNetId() {
+ return netId;
+ }
+
+ /**
* Returns a netid marked with the Private DNS bypass flag.
*
* This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag
@@ -502,7 +514,7 @@
}
/** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(NetworkProto.NET_ID, netId);
proto.end(token);
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9cd4f53..327e42b 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,48 +16,112 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * A Utility class for handling for communicating between bearer-specific
+ * A utility class for handling for communicating between bearer-specific
* code and ConnectivityService.
*
+ * An agent manages the life cycle of a network. A network starts its
+ * life cycle when {@link register} is called on NetworkAgent. The network
+ * is then connecting. When full L3 connectivity has been established,
+ * the agent shoud call {@link markConnected} to inform the system that
+ * this network is ready to use. When the network disconnects its life
+ * ends and the agent should call {@link unregister}, at which point the
+ * system will clean up and free resources.
+ * Any reconnection becomes a new logical network, so after a network
+ * is disconnected the agent cannot be used any more. Network providers
+ * should create a new NetworkAgent instance to handle new connections.
+ *
* A bearer may have more than one NetworkAgent if it can simultaneously
* support separate networks (IMS / Internet / MMS Apns on cellular, or
* perhaps connections with different SSID or P2P for Wi-Fi).
*
+ * This class supports methods to start and stop sending keepalive packets.
+ * Keepalive packets are typically sent at periodic intervals over a network
+ * with NAT when there is no other traffic to avoid the network forcefully
+ * closing the connection. NetworkAgents that manage technologies that
+ * have hardware support for keepalive should implement the related
+ * methods to save battery life. NetworkAgent that cannot get support
+ * without waking up the CPU should not, as this would be prohibitive in
+ * terms of battery - these agents should simply not override the related
+ * methods, which results in the implementation returning
+ * {@link SocketKeepalive.ERROR_UNSUPPORTED} as appropriate.
+ *
+ * Keepalive packets need to be sent at relatively frequent intervals
+ * (a few seconds to a few minutes). As the contents of keepalive packets
+ * depend on the current network status, hardware needs to be configured
+ * to send them and has a limited amount of memory to do so. The HAL
+ * formalizes this as slots that an implementation can configure to send
+ * the correct packets. Devices typically have a small number of slots
+ * per radio technology, and the specific number of slots for each
+ * technology is specified in configuration files.
+ * {@see SocketKeepalive} for details.
+ *
* @hide
*/
-public abstract class NetworkAgent extends Handler {
- // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
- // an exception.
- public final int netId;
+@SystemApi
+public abstract class NetworkAgent {
+ /**
+ * The {@link Network} corresponding to this object.
+ */
+ @Nullable
+ private volatile Network mNetwork;
+ // Whether this NetworkAgent is using the legacy (never unhidden) API. The difference is
+ // that the legacy API uses NetworkInfo to convey the state, while the current API is
+ // exposing methods to manage it and generate it internally instead.
+ // TODO : remove this as soon as all agents have been converted.
+ private final boolean mIsLegacy;
+
+ private final Handler mHandler;
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private final Context mContext;
- private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+ private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500;
- private boolean mPollLceScheduled = false;
- private AtomicBoolean mPollLcePending = new AtomicBoolean(false);
- public final int mFactorySerialNumber;
+ private boolean mBandwidthUpdateScheduled = false;
+ private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+ // Not used by legacy agents. Non-legacy agents use this to convert the NetworkAgent system API
+ // into the internal API of ConnectivityService.
+ @NonNull
+ private NetworkInfo mNetworkInfo;
+ @NonNull
+ private final Object mRegisterLock = new Object();
+
+ /**
+ * The ID of the {@link NetworkProvider} that created this object, or
+ * {@link NetworkProvider#ID_NONE} if unknown.
+ * @hide
+ */
+ public final int providerId;
private static final int BASE = Protocol.BASE_NETWORK_AGENT;
@@ -65,6 +129,7 @@
* Sent by ConnectivityService to the NetworkAgent to inform it of
* suspected connectivity problems on its network. The NetworkAgent
* should take steps to verify and correct connectivity.
+ * @hide
*/
public static final int CMD_SUSPECT_BAD = BASE;
@@ -73,6 +138,7 @@
* ConnectivityService to pass the current NetworkInfo (connection state).
* Sent when the NetworkInfo changes, mainly due to change of state.
* obj = NetworkInfo
+ * @hide
*/
public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;
@@ -80,6 +146,7 @@
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkCapabilties.
* obj = NetworkCapabilities
+ * @hide
*/
public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
@@ -87,18 +154,22 @@
* Sent by the NetworkAgent to ConnectivityService to pass the current
* NetworkProperties.
* obj = NetworkProperties
+ * @hide
*/
public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
- /* centralize place where base network score, and network score scaling, will be
+ /**
+ * Centralize the place where base network score, and network score scaling, will be
* stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
+ * @hide
*/
public static final int WIFI_BASE_SCORE = 60;
/**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* network score.
- * obj = network score Integer
+ * arg1 = network score int
+ * @hide
*/
public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
@@ -111,12 +182,41 @@
* obj = Bundle containing map from {@code REDIRECT_URL_KEY} to {@code String}
* representing URL that Internet probe was redirect to, if it was redirected,
* or mapping to {@code null} otherwise.
+ * @hide
*/
public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7;
+
+ /**
+ * Network validation suceeded.
+ * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}.
+ */
+ public static final int VALIDATION_STATUS_VALID = 1;
+
+ /**
+ * Network validation was attempted and failed. This may be received more than once as
+ * subsequent validation attempts are made.
+ */
+ public static final int VALIDATION_STATUS_NOT_VALID = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+ VALIDATION_STATUS_VALID,
+ VALIDATION_STATUS_NOT_VALID
+ })
+ public @interface ValidationStatus {}
+
+ // TODO: remove.
+ /** @hide */
public static final int VALID_NETWORK = 1;
+ /** @hide */
public static final int INVALID_NETWORK = 2;
+ /**
+ * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
+ * @hide
+ */
public static String REDIRECT_URL_KEY = "redirect URL";
/**
@@ -125,6 +225,7 @@
* CONNECTED so it can be given special treatment at that time.
*
* obj = boolean indicating whether to use this network even if unvalidated
+ * @hide
*/
public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
@@ -135,12 +236,14 @@
* responsibility to remember it.
*
* arg1 = 1 if true, 0 if false
+ * @hide
*/
public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
/**
* Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
* the underlying network connection for updated bandwidth information.
+ * @hide
*/
public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
@@ -148,20 +251,22 @@
* Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
* periodically on the given interval.
*
- * arg1 = the slot number of the keepalive to start
+ * arg1 = the hardware slot number of the keepalive to start
* arg2 = interval in seconds
* obj = KeepalivePacketData object describing the data to be sent
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ * @hide
*/
public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
/**
* Requests that the specified keepalive packet be stopped.
*
- * arg1 = slot number of the keepalive to stop.
+ * arg1 = hardware slot number of the keepalive to stop.
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ * @hide
*/
public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
@@ -173,36 +278,18 @@
* This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
* so that the app's {@link SocketKeepalive.Callback} methods can be called.
*
- * arg1 = slot number of the keepalive
+ * arg1 = hardware slot number of the keepalive
* arg2 = error code
+ * @hide
*/
public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
- // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved
- /**
- * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
- *
- * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
- * remote site will send ACK packets in response to the keepalive packets, the firmware also
- * needs to be configured to properly filter the ACKs to prevent the system from waking up.
- * This does not happen with UDP, so this message is TCP-specific.
- * arg1 = slot number of the keepalive to filter for.
- * obj = the keepalive packet to send repeatedly.
- */
- public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
-
- /**
- * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
- * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
- * arg1 = slot number of the keepalive packet filter to remove.
- */
- public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
-
/**
* Sent by ConnectivityService to inform this network transport of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
*
* obj = int[] describing signal strength thresholds.
+ * @hide
*/
public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
@@ -210,156 +297,321 @@
* Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
* automatically reconnecting to this network (e.g. via autojoin). Happens
* when user selects "No" option on the "Stay connected?" dialog box.
+ * @hide
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
- // TODO : remove these two constructors. They are a stopgap measure to help sheperding a number
- // of dependent changes that would conflict throughout the automerger graph. Having these
- // temporarily helps with the process of going through with all these dependent changes across
- // the entire tree.
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
+ *
+ * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
+ * remote site will send ACK packets in response to the keepalive packets, the firmware also
+ * needs to be configured to properly filter the ACKs to prevent the system from waking up.
+ * This does not happen with UDP, so this message is TCP-specific.
+ * arg1 = hardware slot number of the keepalive to filter for.
+ * obj = the keepalive packet to send repeatedly.
+ * @hide
+ */
+ public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
+
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
+ * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
+ * arg1 = hardware slot number of the keepalive packet filter to remove.
+ * @hide
+ */
+ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
+
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
- this(looper, context, logTag, ni, nc, lp, score, null, NetworkFactory.SerialNumber.NONE);
- }
- public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
- this(looper, context, logTag, ni, nc, lp, score, misc, NetworkFactory.SerialNumber.NONE);
+ this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
+ // Register done by the constructor called in the previous line
}
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, int factorySerialNumber) {
- this(looper, context, logTag, ni, nc, lp, score, null, factorySerialNumber);
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
+ this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
+ // Register done by the constructor called in the previous line
}
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
- int factorySerialNumber) {
- super(looper);
+ NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
+ this(looper, context, logTag, ni, nc, lp, score, null, providerId);
+ // Register done by the constructor called in the previous line
+ }
+
+ /** @hide TODO: remove and replace usage with the public constructor. */
+ public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
+ int providerId) {
+ this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
+ register();
+ }
+
+ private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
+ // The subtype can be changed with (TODO) setLegacySubtype, but it starts
+ // with the type and an empty description.
+ final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, "");
+ ni.setIsAvailable(true);
+ ni.setExtraInfo(config.getLegacyExtraInfo());
+ return ni;
+ }
+
+ /**
+ * Create a new network agent.
+ * @param context a {@link Context} to get system services from.
+ * @param looper the {@link Looper} on which to invoke the callbacks.
+ * @param logTag the tag for logs
+ * @param nc the initial {@link NetworkCapabilities} of this network. Update with
+ * sendNetworkCapabilities.
+ * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties.
+ * @param score the initial score of this network. Update with sendNetworkScore.
+ * @param config an immutable {@link NetworkAgentConfig} for this agent.
+ * @param provider the {@link NetworkProvider} managing this agent.
+ */
+ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+ @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+ this(looper, context, logTag, nc, lp, score, config,
+ provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
+ getLegacyNetworkInfo(config), false /* legacy */);
+ }
+
+ private static class InitialConfiguration {
+ public final Context context;
+ public final NetworkCapabilities capabilities;
+ public final LinkProperties properties;
+ public final int score;
+ public final NetworkAgentConfig config;
+ public final NetworkInfo info;
+ InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
+ @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
+ @NonNull NetworkInfo info) {
+ this.context = context;
+ this.capabilities = capabilities;
+ this.properties = properties;
+ this.score = score;
+ this.config = config;
+ this.info = info;
+ }
+ }
+ private volatile InitialConfiguration mInitialConfiguration;
+
+ private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+ @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni,
+ boolean legacy) {
+ mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
- mContext = context;
- mFactorySerialNumber = factorySerialNumber;
+ mIsLegacy = legacy;
+ mNetworkInfo = new NetworkInfo(ni);
+ this.providerId = providerId;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
- if (VDBG) log("Registering NetworkAgent");
- ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
- new LinkProperties(lp), new NetworkCapabilities(nc), score, misc,
- factorySerialNumber);
+ mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc),
+ new LinkProperties(lp), score, config, ni);
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- if (mAsyncChannel != null) {
- log("Received new connection while already connected!");
- } else {
- if (VDBG) log("NetworkAgent fully connected");
- AsyncChannel ac = new AsyncChannel();
- ac.connected(null, this, msg.replyTo);
- ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_SUCCESSFUL);
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = ac;
- for (Message m : mPreConnectedQueue) {
- ac.sendMessage(m);
- }
- mPreConnectedQueue.clear();
- }
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
- if (VDBG) log("CMD_CHANNEL_DISCONNECT");
- if (mAsyncChannel != null) mAsyncChannel.disconnect();
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- if (DBG) log("NetworkAgent channel lost");
- // let the client know CS is done with us.
- unwanted();
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = null;
- }
- break;
- }
- case CMD_SUSPECT_BAD: {
- log("Unhandled Message " + msg);
- break;
- }
- case CMD_REQUEST_BANDWIDTH_UPDATE: {
- long currentTimeMs = System.currentTimeMillis();
- if (VDBG) {
- log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
- }
- if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
- mPollLceScheduled = false;
- if (mPollLcePending.getAndSet(true) == false) {
- pollLceData();
- }
- } else {
- // deliver the request at a later time rather than discard it completely.
- if (!mPollLceScheduled) {
- long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
- currentTimeMs + 1;
- mPollLceScheduled = sendEmptyMessageDelayed(
- CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
- }
- }
- break;
- }
- case CMD_REPORT_NETWORK_STATUS: {
- String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
- if (VDBG) {
- log("CMD_REPORT_NETWORK_STATUS(" +
- (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
- }
- networkStatus(msg.arg1, redirectUrl);
- break;
- }
- case CMD_SAVE_ACCEPT_UNVALIDATED: {
- saveAcceptUnvalidated(msg.arg1 != 0);
- break;
- }
- case CMD_START_SOCKET_KEEPALIVE: {
- startSocketKeepalive(msg);
- break;
- }
- case CMD_STOP_SOCKET_KEEPALIVE: {
- stopSocketKeepalive(msg);
- break;
- }
+ private class NetworkAgentHandler extends Handler {
+ NetworkAgentHandler(Looper looper) {
+ super(looper);
+ }
- case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
- ArrayList<Integer> thresholds =
- ((Bundle) msg.obj).getIntegerArrayList("thresholds");
- // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
- // rather than convert to int[].
- int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
- for (int i = 0; i < intThresholds.length; i++) {
- intThresholds[i] = thresholds.get(i);
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG) log("NetworkAgent fully connected");
+ AsyncChannel ac = new AsyncChannel();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
}
- setSignalStrengthThresholds(intThresholds);
- break;
- }
- case CMD_PREVENT_AUTOMATIC_RECONNECT: {
- preventAutomaticReconnect();
- break;
- }
- case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
- addKeepalivePacketFilter(msg);
- break;
- }
- case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
- removeKeepalivePacketFilter(msg);
- break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+ if (mAsyncChannel != null) mAsyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (DBG) log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+ onNetworkUnwanted();
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
+ break;
+ }
+ case CMD_SUSPECT_BAD: {
+ log("Unhandled Message " + msg);
+ break;
+ }
+ case CMD_REQUEST_BANDWIDTH_UPDATE: {
+ long currentTimeMs = System.currentTimeMillis();
+ if (VDBG) {
+ log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+ }
+ if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+ mBandwidthUpdateScheduled = false;
+ if (!mBandwidthUpdatePending.getAndSet(true)) {
+ onBandwidthUpdateRequested();
+ }
+ } else {
+ // deliver the request at a later time rather than discard it completely.
+ if (!mBandwidthUpdateScheduled) {
+ long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
+ - currentTimeMs + 1;
+ mBandwidthUpdateScheduled = sendEmptyMessageDelayed(
+ CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+ }
+ }
+ break;
+ }
+ case CMD_REPORT_NETWORK_STATUS: {
+ String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS("
+ + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+ + redirectUrl);
+ }
+ Uri uri = null;
+ try {
+ if (null != redirectUrl) {
+ uri = Uri.parse(redirectUrl);
+ }
+ } catch (Exception e) {
+ Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e);
+ }
+ onValidationStatus(msg.arg1 /* status */, uri);
+ break;
+ }
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ onSaveAcceptUnvalidated(msg.arg1 != 0);
+ break;
+ }
+ case CMD_START_SOCKET_KEEPALIVE: {
+ onStartSocketKeepalive(msg.arg1 /* slot */,
+ Duration.ofSeconds(msg.arg2) /* interval */,
+ (KeepalivePacketData) msg.obj /* packet */);
+ break;
+ }
+ case CMD_STOP_SOCKET_KEEPALIVE: {
+ onStopSocketKeepalive(msg.arg1 /* slot */);
+ break;
+ }
+
+ case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+ ArrayList<Integer> thresholds =
+ ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+ // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+ // rather than convert to int[].
+ int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+ for (int i = 0; i < intThresholds.length; i++) {
+ intThresholds[i] = thresholds.get(i);
+ }
+ onSignalStrengthThresholdsUpdated(intThresholds);
+ break;
+ }
+ case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+ onAutomaticReconnectDisabled();
+ break;
+ }
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+ onAddKeepalivePacketFilter(msg.arg1 /* slot */,
+ (KeepalivePacketData) msg.obj /* packet */);
+ break;
+ }
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+ onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
+ break;
+ }
}
}
}
+ /**
+ * Register this network agent with ConnectivityService.
+ *
+ * This method can only be called once per network agent.
+ *
+ * @return the Network associated with this network agent (which can also be obtained later
+ * by calling getNetwork() on this agent).
+ * @throws IllegalStateException thrown by the system server if this network agent is
+ * already registered.
+ */
+ @NonNull
+ public Network register() {
+ if (VDBG) log("Registering NetworkAgent");
+ synchronized (mRegisterLock) {
+ if (mNetwork != null) {
+ throw new IllegalStateException("Agent already registered");
+ }
+ final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
+ new NetworkInfo(mInitialConfiguration.info),
+ mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+ mInitialConfiguration.score, mInitialConfiguration.config, providerId);
+ mInitialConfiguration = null; // All this memory can now be GC'd
+ }
+ return mNetwork;
+ }
+
+ /**
+ * Register this network agent with a testing harness.
+ *
+ * The returned Messenger sends messages to the Handler. This allows a test to send
+ * this object {@code CMD_*} messages as if they came from ConnectivityService, which
+ * is useful for testing the behavior.
+ *
+ * @hide
+ */
+ public Messenger registerForTest(final Network network) {
+ log("Registering NetworkAgent for test");
+ synchronized (mRegisterLock) {
+ mNetwork = network;
+ mInitialConfiguration = null;
+ }
+ return new Messenger(mHandler);
+ }
+
+ /**
+ * Waits for the handler to be idle.
+ * This is useful for testing, and has smaller scope than an accessor to mHandler.
+ * TODO : move the implementation in common library with the tests
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean waitForIdle(final long timeoutMs) {
+ final ConditionVariable cv = new ConditionVariable(false);
+ mHandler.post(cv::open);
+ return cv.block(timeoutMs);
+ }
+
+ /**
+ * @return The Network associated with this agent, or null if it's not registered yet.
+ */
+ @Nullable
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
private void queueOrSendMessage(int what, Object obj) {
queueOrSendMessage(what, 0, 0, obj);
}
@@ -388,42 +640,128 @@
}
/**
- * Called by the bearer code when it has new LinkProperties data.
+ * Must be called by the agent when the network's {@link LinkProperties} change.
+ * @param linkProperties the new LinkProperties.
*/
- public void sendLinkProperties(LinkProperties linkProperties) {
+ public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+ Objects.requireNonNull(linkProperties);
queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
- * Called by the bearer code when it has new NetworkInfo data.
+ * Inform ConnectivityService that this agent has now connected.
+ * Call {@link #unregister} to disconnect.
+ */
+ public void markConnected() {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException(
+ "Legacy agents can't call markConnected.");
+ }
+ // |reason| cannot be used by the non-legacy agents
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
+ mNetworkInfo.getExtraInfo());
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Unregister this network agent.
+ *
+ * This signals the network has disconnected and ends its lifecycle. After this is called,
+ * the network is torn down and this agent can no longer be used.
+ */
+ public void unregister() {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException("Legacy agents can't call unregister.");
+ }
+ // When unregistering an agent nobody should use the extrainfo (or reason) any more.
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
+ null /* extraInfo */);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Change the legacy subtype of this network agent.
+ *
+ * This is only for backward compatibility and should not be used by non-legacy network agents,
+ * or agents that did not use to set a subtype. As such, only TYPE_MOBILE type agents can use
+ * this and others will be thrown an exception if they try.
+ *
+ * @deprecated this is for backward compatibility only.
+ * @param legacySubtype the legacy subtype.
+ * @hide
+ */
+ @Deprecated
+ public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException("Legacy agents can't call setLegacySubtype.");
+ }
+ mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Set the ExtraInfo of this network agent.
+ *
+ * This sets the ExtraInfo field inside the NetworkInfo returned by legacy public API and the
+ * broadcasts about the corresponding Network.
+ * This is only for backward compatibility and should not be used by non-legacy network agents,
+ * who will be thrown an exception if they try. The extra info should only be :
+ * <ul>
+ * <li>For cellular agents, the APN name.</li>
+ * <li>For ethernet agents, the interface name.</li>
+ * </ul>
+ *
+ * @deprecated this is for backward compatibility only.
+ * @param extraInfo the ExtraInfo.
+ * @hide
+ */
+ @Deprecated
+ public void setLegacyExtraInfo(@Nullable final String extraInfo) {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException("Legacy agents can't call setLegacyExtraInfo.");
+ }
+ mNetworkInfo.setExtraInfo(extraInfo);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Must be called by the agent when it has a new NetworkInfo object.
+ * @hide TODO: expose something better.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public void sendNetworkInfo(NetworkInfo networkInfo) {
+ public final void sendNetworkInfo(NetworkInfo networkInfo) {
+ if (!mIsLegacy) {
+ throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
+ }
queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
}
/**
- * Called by the bearer code when it has new NetworkCapabilities data.
+ * Must be called by the agent when the network's {@link NetworkCapabilities} change.
+ * @param networkCapabilities the new NetworkCapabilities.
*/
- public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
- mPollLcePending.set(false);
+ public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ Objects.requireNonNull(networkCapabilities);
+ mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
new NetworkCapabilities(networkCapabilities));
}
/**
- * Called by the bearer code when it has a new score for this network.
+ * Must be called by the agent to update the score of this network.
+ *
+ * @param score the new score, between 0 and 99.
*/
- public void sendNetworkScore(int score) {
+ public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
if (score < 0) {
throw new IllegalArgumentException("Score must be >= 0");
}
- queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score, 0);
+ queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score, 0);
}
/**
- * Called by the bearer to indicate this network was manually selected by the user.
+ * Must be called by the agent to indicate this network was manually selected by the user.
* This should be called before the NetworkInfo is marked CONNECTED so that this
* Network can be given special treatment at that time. If {@code acceptUnvalidated} is
* {@code true}, then the system will switch to this network. If it is {@code false} and the
@@ -432,21 +770,31 @@
* {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
* calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
* {@link #saveAcceptUnvalidated} to respect the user's choice.
+ * @hide should move to NetworkAgentConfig.
*/
public void explicitlySelected(boolean acceptUnvalidated) {
explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
}
/**
- * Called by the bearer to indicate this network was manually selected by the user.
- * This should be called before the NetworkInfo is marked CONNECTED so that this
- * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
- * {@code true}, then the system will switch to this network. If it is {@code false} and the
- * network cannot be validated, the system will ask the user whether to switch to this network.
- * If the user confirms and selects "don't ask again", then the system will call
- * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
- * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
- * {@link #saveAcceptUnvalidated} to respect the user's choice.
+ * Must be called by the agent to indicate whether the network was manually selected by the
+ * user. This should be called before the network becomes connected, so it can be given
+ * special treatment when it does.
+ *
+ * If {@code explicitlySelected} is {@code true}, and {@code acceptUnvalidated} is {@code true},
+ * then the system will switch to this network. If {@code explicitlySelected} is {@code true}
+ * and {@code acceptUnvalidated} is {@code false}, and the network cannot be validated, the
+ * system will ask the user whether to switch to this network. If the user confirms and selects
+ * "don't ask again", then the system will call {@link #saveAcceptUnvalidated} to persist the
+ * user's choice. Thus, if the transport ever calls this method with {@code explicitlySelected}
+ * set to {@code true} and {@code acceptUnvalidated} set to {@code false}, it must also
+ * implement {@link #saveAcceptUnvalidated} to respect the user's choice.
+ *
+ * If {@code explicitlySelected} is {@code false} and {@code acceptUnvalidated} is
+ * {@code true}, the system will interpret this as the user having accepted partial connectivity
+ * on this network. Thus, the system will switch to the network and consider it validated even
+ * if it only provides partial connectivity, but the network is not otherwise treated specially.
+ * @hide should move to NetworkAgentConfig.
*/
public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED,
@@ -460,73 +808,141 @@
* as well, either canceling NetworkRequests or altering their score such that this
* network won't be immediately requested again.
*/
- abstract protected void unwanted();
+ public void onNetworkUnwanted() {
+ unwanted();
+ }
+ /** @hide TODO delete once subclasses have moved to onNetworkUnwanted. */
+ protected void unwanted() {
+ }
/**
* Called when ConnectivityService request a bandwidth update. The parent factory
* shall try to overwrite this method and produce a bandwidth update if capable.
+ * @hide
*/
+ public void onBandwidthUpdateRequested() {
+ pollLceData();
+ }
+ /** @hide TODO delete once subclasses have moved to onBandwidthUpdateRequested. */
protected void pollLceData() {
}
/**
* Called when the system determines the usefulness of this network.
*
- * Networks claiming internet connectivity will have their internet
- * connectivity verified.
+ * The system attempts to validate Internet connectivity on networks that provide the
+ * {@link NetworkCapabilities#NET_CAPABILITY_INTERNET} capability.
*
* Currently there are two possible values:
- * {@code VALID_NETWORK} if the system is happy with the connection,
- * {@code INVALID_NETWORK} if the system is not happy.
- * TODO - add indications of captive portal-ness and related success/failure,
- * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+ * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
+ * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
*
- * This may be called multiple times as the network status changes and may
- * generate false negatives if we lose ip connectivity before the link is torn down.
+ * This is guaranteed to be called again when the network status changes, but the system
+ * may also call this multiple times even if the status does not change.
*
- * @param status one of {@code VALID_NETWORK} or {@code INVALID_NETWORK}.
- * @param redirectUrl If the Internet probe was redirected, this is the destination it was
- * redirected to, otherwise {@code null}.
+ * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
+ * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal),
+ * this is the destination the probes are being redirected to, otherwise {@code null}.
*/
+ public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
+ networkStatus(status, null == redirectUri ? "" : redirectUri.toString());
+ }
+ /** @hide TODO delete once subclasses have moved to onValidationStatus */
protected void networkStatus(int status, String redirectUrl) {
}
+
/**
* Called when the user asks to remember the choice to use this network even if unvalidated.
* The transport is responsible for remembering the choice, and the next time the user connects
* to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
* This method will only be called if {@link #explicitlySelected} was called with
* {@code acceptUnvalidated} set to {@code false}.
+ * @param accept whether the user wants to use the network even if unvalidated.
*/
+ public void onSaveAcceptUnvalidated(boolean accept) {
+ saveAcceptUnvalidated(accept);
+ }
+ /** @hide TODO delete once subclasses have moved to onSaveAcceptUnvalidated */
protected void saveAcceptUnvalidated(boolean accept) {
}
/**
* Requests that the network hardware send the specified packet at the specified interval.
+ *
+ * @param slot the hardware slot on which to start the keepalive.
+ * @param interval the interval between packets, between 10 and 3600. Note that this API
+ * does not support sub-second precision and will round off the request.
+ * @param packet the packet to send.
*/
+ // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should
+ // not be exposed as constants because they may change in the future (API guideline 4.8)
+ // and should have getters if exposed at all. Getters can't be used in the annotation,
+ // so the values unfortunately need to be copied.
+ public void onStartSocketKeepalive(int slot, @NonNull Duration interval,
+ @NonNull KeepalivePacketData packet) {
+ final long intervalSeconds = interval.getSeconds();
+ if (intervalSeconds < SocketKeepalive.MIN_INTERVAL_SEC
+ || intervalSeconds > SocketKeepalive.MAX_INTERVAL_SEC) {
+ throw new IllegalArgumentException("Interval needs to be comprised between "
+ + SocketKeepalive.MIN_INTERVAL_SEC + " and " + SocketKeepalive.MAX_INTERVAL_SEC
+ + " but was " + intervalSeconds);
+ }
+ final Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot,
+ (int) intervalSeconds, packet);
+ startSocketKeepalive(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onStartSocketKeepalive */
protected void startSocketKeepalive(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
}
/**
- * Requests that the network hardware send the specified packet at the specified interval.
+ * Requests that the network hardware stop a previously-started keepalive.
+ *
+ * @param slot the hardware slot on which to stop the keepalive.
*/
+ public void onStopSocketKeepalive(int slot) {
+ Message msg = mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0, null);
+ stopSocketKeepalive(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onStopSocketKeepalive */
protected void stopSocketKeepalive(Message msg) {
onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED);
}
/**
- * Called by the network when a socket keepalive event occurs.
+ * Must be called by the agent when a socket keepalive event occurs.
+ *
+ * @param slot the hardware slot on which the event occurred.
+ * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
+ * or SocketKeepalive.SUCCESS constants.
*/
+ public final void sendSocketKeepaliveEvent(int slot,
+ @SocketKeepalive.KeepaliveEvent int event) {
+ queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
+ }
+ /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
public void onSocketKeepaliveEvent(int slot, int reason) {
- queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
+ sendSocketKeepaliveEvent(slot, reason);
}
/**
* Called by ConnectivityService to add specific packet filter to network hardware to block
- * ACKs matching the sent keepalive packets. Implementations that support this feature must
- * override this method.
+ * replies (e.g., TCP ACKs) matching the sent keepalive packets. Implementations that support
+ * this feature must override this method.
+ *
+ * @param slot the hardware slot on which the keepalive should be sent.
+ * @param packet the packet that is being sent.
*/
+ public void onAddKeepalivePacketFilter(int slot, @NonNull KeepalivePacketData packet) {
+ Message msg = mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0, packet);
+ addKeepalivePacketFilter(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onAddKeepalivePacketFilter */
protected void addKeepalivePacketFilter(Message msg) {
}
@@ -534,14 +950,37 @@
* Called by ConnectivityService to remove a packet filter installed with
* {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
* must override this method.
+ *
+ * @param slot the hardware slot on which the keepalive is being sent.
*/
+ public void onRemoveKeepalivePacketFilter(int slot) {
+ Message msg = mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0, null);
+ removeKeepalivePacketFilter(msg);
+ msg.recycle();
+ }
+ /** @hide TODO delete once subclasses have moved to onRemoveKeepalivePacketFilter */
protected void removeKeepalivePacketFilter(Message msg) {
}
/**
- * Called by ConnectivityService to inform this network transport of signal strength thresholds
+ * Called by ConnectivityService to inform this network agent of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+ *
+ * When the system updates the list of thresholds that should wake up the CPU for a
+ * given agent it will call this method on the agent. The agent that implement this
+ * should implement it in hardware so as to ensure the CPU will be woken up on breach.
+ * Agents are expected to react to a breach by sending an updated NetworkCapabilities
+ * object with the appropriate signal strength to sendNetworkCapabilities.
+ *
+ * The specific units are bearer-dependent. See details on the units and requests in
+ * {@link NetworkCapabilities.Builder#setSignalStrength}.
+ *
+ * @param thresholds the array of thresholds that should trigger wakeups.
*/
+ public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
+ setSignalStrengthThresholds(thresholds);
+ }
+ /** @hide TODO delete once subclasses have moved to onSetSignalStrengthThresholds */
protected void setSignalStrengthThresholds(int[] thresholds) {
}
@@ -551,9 +990,14 @@
* responsible for making sure the device does not automatically reconnect to the same network
* after the {@code unwanted} call.
*/
+ public void onAutomaticReconnectDisabled() {
+ preventAutomaticReconnect();
+ }
+ /** @hide TODO delete once subclasses have moved to onAutomaticReconnectDisabled */
protected void preventAutomaticReconnect() {
}
+ /** @hide */
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
new file mode 100644
index 0000000..fe1268d
--- /dev/null
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2014 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Allows a network transport to provide the system with policy and configuration information about
+ * a particular network when registering a {@link NetworkAgent}. This information cannot change once the agent is registered.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NetworkAgentConfig implements Parcelable {
+
+ /**
+ * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+ * VPN. This is set by a {@link VpnService} and used by
+ * {@link ConnectivityManager} when creating a VPN.
+ *
+ * @hide
+ */
+ public boolean allowBypass;
+
+ /**
+ * Set if the network was manually/explicitly connected to by the user either from settings
+ * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
+ * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
+ * connect to a particular access point is also explicit, though this may change in the future
+ * as we want apps to use the multinetwork apis.
+ *
+ * @hide
+ */
+ public boolean explicitlySelected;
+
+ /**
+ * @return whether this network was explicitly selected by the user.
+ */
+ public boolean isExplicitlySelected() {
+ return explicitlySelected;
+ }
+
+ /**
+ * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
+ * appropriate value based on previous user choice.
+ *
+ * TODO : rename this field to match its accessor
+ * @hide
+ */
+ public boolean acceptUnvalidated;
+
+ /**
+ * @return whether the system should accept this network even if it doesn't validate.
+ */
+ public boolean isUnvalidatedConnectivityAcceptable() {
+ return acceptUnvalidated;
+ }
+
+ /**
+ * Whether the user explicitly set that this network should be validated even if presence of
+ * only partial internet connectivity.
+ *
+ * TODO : rename this field to match its accessor
+ * @hide
+ */
+ public boolean acceptPartialConnectivity;
+
+ /**
+ * @return whether the system should validate this network even if it only offers partial
+ * Internet connectivity.
+ */
+ public boolean isPartialConnectivityAcceptable() {
+ return acceptPartialConnectivity;
+ }
+
+ /**
+ * Set to avoid surfacing the "Sign in to network" notification.
+ * if carrier receivers/apps are registered to handle the carrier-specific provisioning
+ * procedure, a carrier specific provisioning notification will be placed.
+ * only one notification should be displayed. This field is set based on
+ * which notification should be used for provisioning.
+ *
+ * @hide
+ */
+ public boolean provisioningNotificationDisabled;
+
+ /**
+ *
+ * @return whether the sign in to network notification is enabled by this configuration.
+ * @hide
+ */
+ public boolean isProvisioningNotificationEnabled() {
+ return !provisioningNotificationDisabled;
+ }
+
+ /**
+ * For mobile networks, this is the subscriber ID (such as IMSI).
+ *
+ * @hide
+ */
+ public String subscriberId;
+
+ /**
+ * @return the subscriber ID, or null if none.
+ * @hide
+ */
+ @Nullable
+ public String getSubscriberId() {
+ return subscriberId;
+ }
+
+ /**
+ * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+ * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+ *
+ * @hide
+ */
+ public boolean skip464xlat;
+
+ /**
+ * @return whether NAT64 prefix detection is enabled.
+ * @hide
+ */
+ public boolean isNat64DetectionEnabled() {
+ return !skip464xlat;
+ }
+
+ /**
+ * The legacy type of this network agent, or TYPE_NONE if unset.
+ * @hide
+ */
+ public int legacyType = ConnectivityManager.TYPE_NONE;
+
+ /**
+ * @return the legacy type
+ */
+ @ConnectivityManager.LegacyNetworkType
+ public int getLegacyType() {
+ return legacyType;
+ }
+
+ /**
+ * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+ * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+ *
+ * This is not parceled, because it would not make sense.
+ *
+ * @hide
+ */
+ public transient boolean hasShownBroken;
+
+ /**
+ * The name of the legacy network type. It's a free-form string used in logging.
+ * @hide
+ */
+ @NonNull
+ public String legacyTypeName = "";
+
+ /**
+ * @return the name of the legacy network type. It's a free-form string used in logging.
+ */
+ @NonNull
+ public String getLegacyTypeName() {
+ return legacyTypeName;
+ }
+
+ /**
+ * The legacy extra info of the agent. The extra info should only be :
+ * <ul>
+ * <li>For cellular agents, the APN name.</li>
+ * <li>For ethernet agents, the interface name.</li>
+ * </ul>
+ * @hide
+ */
+ @NonNull
+ private String mLegacyExtraInfo = "";
+
+ /**
+ * The legacy extra info of the agent.
+ * @hide
+ */
+ @NonNull
+ public String getLegacyExtraInfo() {
+ return mLegacyExtraInfo;
+ }
+
+ /** @hide */
+ public NetworkAgentConfig() {
+ }
+
+ /** @hide */
+ public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) {
+ if (nac != null) {
+ allowBypass = nac.allowBypass;
+ explicitlySelected = nac.explicitlySelected;
+ acceptUnvalidated = nac.acceptUnvalidated;
+ acceptPartialConnectivity = nac.acceptPartialConnectivity;
+ subscriberId = nac.subscriberId;
+ provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
+ skip464xlat = nac.skip464xlat;
+ legacyType = nac.legacyType;
+ legacyTypeName = nac.legacyTypeName;
+ mLegacyExtraInfo = nac.mLegacyExtraInfo;
+ }
+ }
+
+ /**
+ * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
+ */
+ public static final class Builder {
+ private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
+
+ /**
+ * Sets whether the network was explicitly selected by the user.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setExplicitlySelected(final boolean explicitlySelected) {
+ mConfig.explicitlySelected = explicitlySelected;
+ return this;
+ }
+
+ /**
+ * Sets whether the system should validate this network even if it is found not to offer
+ * Internet connectivity.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setUnvalidatedConnectivityAcceptable(
+ final boolean unvalidatedConnectivityAcceptable) {
+ mConfig.acceptUnvalidated = unvalidatedConnectivityAcceptable;
+ return this;
+ }
+
+ /**
+ * Sets whether the system should validate this network even if it is found to only offer
+ * partial Internet connectivity.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setPartialConnectivityAcceptable(
+ final boolean partialConnectivityAcceptable) {
+ mConfig.acceptPartialConnectivity = partialConnectivityAcceptable;
+ return this;
+ }
+
+ /**
+ * Sets the subscriber ID for this network.
+ *
+ * @return this builder, to facilitate chaining.
+ * @hide
+ */
+ @NonNull
+ public Builder setSubscriberId(@Nullable String subscriberId) {
+ mConfig.subscriberId = subscriberId;
+ return this;
+ }
+
+ /**
+ * Disables active detection of NAT64 (e.g., via RFC 7050 DNS lookups). Used to save power
+ * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
+ *
+ * @return this builder, to facilitate chaining.
+ * @hide
+ */
+ @NonNull
+ public Builder disableNat64Detection() {
+ mConfig.skip464xlat = true;
+ return this;
+ }
+
+ /**
+ * Disables the "Sign in to network" notification. Used if the network transport will
+ * perform its own carrier-specific provisioning procedure.
+ *
+ * @return this builder, to facilitate chaining.
+ * @hide
+ */
+ @NonNull
+ public Builder disableProvisioningNotification() {
+ mConfig.provisioningNotificationDisabled = true;
+ return this;
+ }
+
+ /**
+ * Sets the legacy type for this network.
+ *
+ * @param legacyType the type
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacyType(int legacyType) {
+ mConfig.legacyType = legacyType;
+ return this;
+ }
+
+ /**
+ * Sets the name of the legacy type of the agent. It's a free-form string used in logging.
+ * @param legacyTypeName the name
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacyTypeName(@NonNull String legacyTypeName) {
+ mConfig.legacyTypeName = legacyTypeName;
+ return this;
+ }
+
+ /**
+ * Sets the legacy extra info of the agent.
+ * @param legacyExtraInfo the legacy extra info.
+ * @return this builder, to facilitate chaining.
+ * @hide
+ */
+ @NonNull
+ public Builder setLegacyExtraInfo(@NonNull String legacyExtraInfo) {
+ mConfig.mLegacyExtraInfo = legacyExtraInfo;
+ return this;
+ }
+
+ /**
+ * Returns the constructed {@link NetworkAgentConfig} object.
+ */
+ @NonNull
+ public NetworkAgentConfig build() {
+ return mConfig;
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final NetworkAgentConfig that = (NetworkAgentConfig) o;
+ return allowBypass == that.allowBypass
+ && explicitlySelected == that.explicitlySelected
+ && acceptUnvalidated == that.acceptUnvalidated
+ && acceptPartialConnectivity == that.acceptPartialConnectivity
+ && provisioningNotificationDisabled == that.provisioningNotificationDisabled
+ && skip464xlat == that.skip464xlat
+ && legacyType == that.legacyType
+ && Objects.equals(subscriberId, that.subscriberId)
+ && Objects.equals(legacyTypeName, that.legacyTypeName)
+ && Objects.equals(mLegacyExtraInfo, that.mLegacyExtraInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(allowBypass, explicitlySelected, acceptUnvalidated,
+ acceptPartialConnectivity, provisioningNotificationDisabled, subscriberId,
+ skip464xlat, legacyType, legacyTypeName, mLegacyExtraInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkAgentConfig {"
+ + " allowBypass = " + allowBypass
+ + ", explicitlySelected = " + explicitlySelected
+ + ", acceptUnvalidated = " + acceptUnvalidated
+ + ", acceptPartialConnectivity = " + acceptPartialConnectivity
+ + ", provisioningNotificationDisabled = " + provisioningNotificationDisabled
+ + ", subscriberId = '" + subscriberId + '\''
+ + ", skip464xlat = " + skip464xlat
+ + ", legacyType = " + legacyType
+ + ", hasShownBroken = " + hasShownBroken
+ + ", legacyTypeName = '" + legacyTypeName + '\''
+ + ", legacyExtraInfo = '" + mLegacyExtraInfo + '\''
+ + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeInt(acceptUnvalidated ? 1 : 0);
+ out.writeInt(acceptPartialConnectivity ? 1 : 0);
+ out.writeString(subscriberId);
+ out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+ out.writeInt(skip464xlat ? 1 : 0);
+ out.writeInt(legacyType);
+ out.writeString(legacyTypeName);
+ out.writeString(mLegacyExtraInfo);
+ }
+
+ public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
+ new Creator<NetworkAgentConfig>() {
+ @Override
+ public NetworkAgentConfig createFromParcel(Parcel in) {
+ NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+ networkAgentConfig.allowBypass = in.readInt() != 0;
+ networkAgentConfig.explicitlySelected = in.readInt() != 0;
+ networkAgentConfig.acceptUnvalidated = in.readInt() != 0;
+ networkAgentConfig.acceptPartialConnectivity = in.readInt() != 0;
+ networkAgentConfig.subscriberId = in.readString();
+ networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0;
+ networkAgentConfig.skip464xlat = in.readInt() != 0;
+ networkAgentConfig.legacyType = in.readInt();
+ networkAgentConfig.legacyTypeName = in.readString();
+ networkAgentConfig.mLegacyExtraInfo = in.readString();
+ return networkAgentConfig;
+ }
+
+ @Override
+ public NetworkAgentConfig[] newArray(int size) {
+ return new NetworkAgentConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3e325b7..004f844 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -16,25 +16,32 @@
package android.net;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -55,12 +62,20 @@
*/
public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
- private static final int INVALID_UID = -1;
+
+ // Set to true when private DNS is broken.
+ private boolean mPrivateDnsBroken;
/**
- * @hide
+ * Uid of the app making the request.
*/
- @UnsupportedAppUsage
+ private int mRequestorUid;
+
+ /**
+ * Package name of the app making the request.
+ */
+ private String mRequestorPackageName;
+
public NetworkCapabilities() {
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -84,8 +99,12 @@
mTransportInfo = null;
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
- mEstablishingVpnAppUid = INVALID_UID;
+ mAdministratorUids = new int[0];
+ mOwnerUid = Process.INVALID_UID;
mSSID = null;
+ mPrivateDnsBroken = false;
+ mRequestorUid = Process.INVALID_UID;
+ mRequestorPackageName = null;
}
/**
@@ -101,9 +120,13 @@
mTransportInfo = nc.mTransportInfo;
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
- mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
+ setAdministratorUids(nc.getAdministratorUids());
+ mOwnerUid = nc.mOwnerUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
+ mPrivateDnsBroken = nc.mPrivateDnsBroken;
+ mRequestorUid = nc.mRequestorUid;
+ mRequestorPackageName = nc.mRequestorPackageName;
}
/**
@@ -146,6 +169,7 @@
NET_CAPABILITY_OEM_PAID,
NET_CAPABILITY_MCX,
NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED,
})
public @interface NetCapability { }
@@ -313,8 +337,16 @@
@SystemApi
public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
+ /**
+ * This capability will be set for networks that are generally metered, but are currently
+ * unmetered, e.g., because the user is in a particular area. This capability can be changed at
+ * any time. When it is removed, applications are responsible for stopping any data transfer
+ * that should not occur on a metered network.
+ */
+ public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -330,7 +362,8 @@
| (1 << NET_CAPABILITY_FOREGROUND)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
- | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+ | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED));
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -394,19 +427,34 @@
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
/**
+ * Capabilities that are allowed for test networks. This list must be set so that it is safe
+ * for an unprivileged user to create a network with these capabilities via shell. As such,
+ * it must never contain capabilities that are generally useful to the system, such as
+ * INTERNET, IMS, SUPL, etc.
+ */
+ private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+ (1 << NET_CAPABILITY_NOT_METERED)
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_NOT_VPN)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+ /**
* Adds the given capability to this {@code NetworkCapability} instance.
- * Multiple capabilities may be applied sequentially. Note that when searching
- * for a network to satisfy a request, all capabilities requested must be satisfied.
- * <p>
- * If the given capability was previously added to the list of unwanted capabilities
- * then the capability will also be removed from the list of unwanted capabilities.
+ * Note that when searching for a network to satisfy a request, all capabilities
+ * requested must be satisfied.
*
* @param capability the capability to be added.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- @UnsupportedAppUsage
public @NonNull NetworkCapabilities addCapability(@NetCapability int capability) {
+ // If the given capability was previously added to the list of unwanted capabilities
+ // then the capability will also be removed from the list of unwanted capabilities.
+ // TODO: Consider adding unwanted capabilities to the public API and mention this
+ // in the documentation.
checkValidCapability(capability);
mNetworkCapabilities |= 1 << capability;
mUnwantedNetworkCapabilities &= ~(1 << capability); // remove from unwanted capability list
@@ -415,9 +463,9 @@
/**
* Adds the given capability to the list of unwanted capabilities of this
- * {@code NetworkCapability} instance. Multiple unwanted capabilities may be applied
- * sequentially. Note that when searching for a network to satisfy a request, the network
- * must not contain any capability from unwanted capability list.
+ * {@code NetworkCapability} instance. Note that when searching for a network to
+ * satisfy a request, the network must not contain any capability from unwanted capability
+ * list.
* <p>
* If the capability was previously added to the list of required capabilities (for
* example, it was there by default or added using {@link #addCapability(int)} method), then
@@ -434,16 +482,14 @@
/**
* Removes (if found) the given capability from this {@code NetworkCapability} instance.
- * <p>
- * Note that this method removes capabilities that were added via {@link #addCapability(int)},
- * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} .
*
* @param capability the capability to be removed.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- @UnsupportedAppUsage
public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
+ // Note that this method removes capabilities that were added via addCapability(int),
+ // addUnwantedCapability(int) or setCapabilities(int[], int[]).
checkValidCapability(capability);
final long mask = ~(1 << capability);
mNetworkCapabilities &= mask;
@@ -454,7 +500,6 @@
/**
* Sets (or clears) the given capability on this {@link NetworkCapabilities}
* instance.
- *
* @hide
*/
public @NonNull NetworkCapabilities setCapability(@NetCapability int capability,
@@ -473,6 +518,7 @@
* @return an array of capability values for this instance.
* @hide
*/
+ @UnsupportedAppUsage
@TestApi
public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities);
@@ -557,6 +603,9 @@
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
+ if (isPrivateDnsBroken()) {
+ return "privateDnsBroken";
+ }
return null;
}
@@ -588,15 +637,13 @@
}
/**
- * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are
- * typically provided by restricted networks.
+ * Deduces that all the capabilities it provides are typically provided by restricted networks
+ * or not.
*
- * TODO: consider:
- * - Renaming it to guessRestrictedCapability and make it set the
- * restricted capability bit in addition to clearing it.
+ * @return {@code true} if the network should be restricted.
* @hide
*/
- public void maybeMarkCapabilitiesRestricted() {
+ public boolean deduceRestrictedCapability() {
// Check if we have any capability that forces the network to be restricted.
final boolean forceRestrictedCapability =
(mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
@@ -610,13 +657,51 @@
final boolean hasRestrictedCapabilities =
(mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
- if (forceRestrictedCapability
- || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) {
+ return forceRestrictedCapability
+ || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities);
+ }
+
+ /**
+ * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if deducing the network is restricted.
+ *
+ * @hide
+ */
+ public void maybeMarkCapabilitiesRestricted() {
+ if (deduceRestrictedCapability()) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
}
/**
+ * Test networks have strong restrictions on what capabilities they can have. Enforce these
+ * restrictions.
+ * @hide
+ */
+ public void restrictCapabilitesForTestNetwork(int creatorUid) {
+ final long originalCapabilities = mNetworkCapabilities;
+ final long originalTransportTypes = mTransportTypes;
+ final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+ final int originalSignalStrength = mSignalStrength;
+ final int originalOwnerUid = getOwnerUid();
+ final int[] originalAdministratorUids = getAdministratorUids();
+ clearAll();
+ mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
+ | (1 << TRANSPORT_TEST);
+ mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+ mNetworkSpecifier = originalSpecifier;
+ mSignalStrength = originalSignalStrength;
+
+ // Only retain the owner and administrator UIDs if they match the app registering the remote
+ // caller that registered the network.
+ if (originalOwnerUid == creatorUid) {
+ setOwnerUid(creatorUid);
+ }
+ if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+ setAdministratorUids(new int[] {creatorUid});
+ }
+ }
+
+ /**
* Representing the transport type. Apps should generally not care about transport. A
* request for a fast internet connection could be satisfied by a number of different
* transports. If any are specified here it will be satisfied a Network that matches
@@ -703,8 +788,15 @@
};
/**
+ * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+ */
+ private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+ // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
+ | 1 << TRANSPORT_ETHERNET;
+
+ /**
* Adds the given transport type to this {@code NetworkCapability} instance.
- * Multiple transports may be applied sequentially. Note that when searching
+ * Multiple transports may be applied. Note that when searching
* for a network to satisfy a request, any listed in the request will satisfy the request.
* For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
* {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
@@ -715,7 +807,6 @@
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- @UnsupportedAppUsage
public @NonNull NetworkCapabilities addTransportType(@Transport int transportType) {
checkValidTransportType(transportType);
mTransportTypes |= 1 << transportType;
@@ -800,31 +891,177 @@
}
/**
- * UID of the app that manages this network, or INVALID_UID if none/unknown.
+ * UID of the app that owns this network, or Process#INVALID_UID if none/unknown.
*
- * This field keeps track of the UID of the app that created this network and is in charge
- * of managing it. In the practice, it is used to store the UID of VPN apps so it is named
- * accordingly, but it may be renamed if other mechanisms are offered for third party apps
- * to create networks.
+ * <p>This field keeps track of the UID of the app that created this network and is in charge of
+ * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
+ * VPN, or Carrier Service app managing a cellular data connection.
*
- * Because this field is only used in the services side (and to avoid apps being able to
- * set this to whatever they want), this field is not parcelled and will not be conserved
- * across the IPC boundary.
- * @hide
+ * <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
+ * reset to Process.INVALID_UID unless all the following conditions are met:
+ *
+ * <p>The caller is the network owner, AND one of the following sets of requirements is met:
+ *
+ * <ol>
+ * <li>The described Network is a VPN
+ * </ol>
+ *
+ * <p>OR:
+ *
+ * <ol>
+ * <li>The calling app is the network owner
+ * <li>The calling app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
+ * This is because the owner UID is location-sensitive. The apps that request a network could
+ * know where the device is if they can tell for sure the system has connected to the network
+ * they requested.
+ *
+ * <p>This is populated by the network agents and for the NetworkCapabilities instance sent by
+ * an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system
+ * server.
*/
- private int mEstablishingVpnAppUid = INVALID_UID;
+ private int mOwnerUid = Process.INVALID_UID;
/**
- * Set the UID of the managing app.
+ * Set the UID of the owner app.
* @hide
*/
- public void setEstablishingVpnAppUid(final int uid) {
- mEstablishingVpnAppUid = uid;
+ public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
+ mOwnerUid = uid;
+ return this;
}
- /** @hide */
- public int getEstablishingVpnAppUid() {
- return mEstablishingVpnAppUid;
+ /**
+ * Retrieves the UID of the app that owns this network.
+ *
+ * <p>For user privacy reasons, this field will only be populated if the following conditions
+ * are met:
+ *
+ * <p>The caller is the network owner, AND one of the following sets of requirements is met:
+ *
+ * <ol>
+ * <li>The described Network is a VPN
+ * </ol>
+ *
+ * <p>OR:
+ *
+ * <ol>
+ * <li>The calling app is the network owner
+ * <li>The calling app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
+ * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
+ * this field cleared out.
+ */
+ public int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ /**
+ * UIDs of packages that are administrators of this network, or empty if none.
+ *
+ * <p>This field tracks the UIDs of packages that have permission to manage this network.
+ *
+ * <p>Network owners will also be listed as administrators.
+ *
+ * <p>For NetworkCapability instances being sent from the System Server, this value MUST be
+ * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
+ * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
+ *
+ * <p>When received from an app in a NetworkRequest this is always cleared out by the system
+ * server. This field is never used for matching NetworkRequests to NetworkAgents.
+ */
+ @NonNull private int[] mAdministratorUids = new int[0];
+
+ /**
+ * Sets the int[] of UIDs that are administrators of this network.
+ *
+ * <p>UIDs included in administratorUids gain administrator privileges over this Network.
+ * Examples of UIDs that should be included in administratorUids are:
+ *
+ * <ul>
+ * <li>Carrier apps with privileges for the relevant subscription
+ * <li>Active VPN apps
+ * <li>Other application groups with a particular Network-related role
+ * </ul>
+ *
+ * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
+ *
+ * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
+ * always be included in administratorUids.
+ *
+ * <p>The administrator UIDs are set by network agents.
+ *
+ * @param administratorUids the UIDs to be set as administrators of this Network.
+ * @throws IllegalArgumentException if duplicate UIDs are contained in administratorUids
+ * @see #mAdministratorUids
+ * @hide
+ */
+ @NonNull
+ public NetworkCapabilities setAdministratorUids(@NonNull final int[] administratorUids) {
+ mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
+ Arrays.sort(mAdministratorUids);
+ for (int i = 0; i < mAdministratorUids.length - 1; i++) {
+ if (mAdministratorUids[i] >= mAdministratorUids[i + 1]) {
+ throw new IllegalArgumentException("All administrator UIDs must be unique");
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Retrieves the UIDs that are administrators of this Network.
+ *
+ * <p>This is only populated in NetworkCapabilities objects that come from network agents for
+ * networks that are managed by specific apps on the system, such as carrier privileged apps or
+ * wifi suggestion apps. This will include the network owner.
+ *
+ * @return the int[] of UIDs that are administrators of this Network
+ * @see #mAdministratorUids
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @TestApi
+ public int[] getAdministratorUids() {
+ return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length);
+ }
+
+ /**
+ * Tests if the set of administrator UIDs of this network is the same as that of the passed one.
+ *
+ * <p>The administrator UIDs must be in sorted order.
+ *
+ * <p>nc is assumed non-null. Else, NPE.
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ public boolean equalsAdministratorUids(@NonNull final NetworkCapabilities nc) {
+ return Arrays.equals(mAdministratorUids, nc.mAdministratorUids);
+ }
+
+ /**
+ * Combine the administrator UIDs of the capabilities.
+ *
+ * <p>This is only legal if either of the administrators lists are empty, or if they are equal.
+ * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs.
+ *
+ * <p>If both administrator lists are non-empty but not equal, they conflict with each other. In
+ * this case, it would not make sense to add them together.
+ */
+ private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
+ if (nc.mAdministratorUids.length == 0) return;
+ if (mAdministratorUids.length == 0) {
+ mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
+ return;
+ }
+ if (!equalsAdministratorUids(nc)) {
+ throw new IllegalStateException("Can't combine two different administrator UID lists");
+ }
}
/**
@@ -845,13 +1082,7 @@
* Sets the upstream bandwidth for this network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
* <p>
- * Note that when used to request a network, this specifies the minimum acceptable.
- * When received as the state of an existing network this specifies the typical
- * first hop bandwidth expected. This is never measured, but rather is inferred
- * from technology type and other link parameters. It could be used to differentiate
- * between very slow 1xRTT cellular links and other faster networks or even between
- * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
- * fast backhauls and slow backhauls.
+ * {@see Builder#setLinkUpstreamBandwidthKbps}
*
* @param upKbps the estimated first hop upstream (device to network) bandwidth.
* @hide
@@ -875,13 +1106,7 @@
* Sets the downstream bandwidth for this network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
* <p>
- * Note that when used to request a network, this specifies the minimum acceptable.
- * When received as the state of an existing network this specifies the typical
- * first hop bandwidth expected. This is never measured, but rather is inferred
- * from technology type and other link parameters. It could be used to differentiate
- * between very slow 1xRTT cellular links and other faster networks or even between
- * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
- * fast backhauls and slow backhauls.
+ * {@see Builder#setLinkUpstreamBandwidthKbps}
*
* @param downKbps the estimated first hop downstream (network to device) bandwidth.
* @hide
@@ -945,7 +1170,8 @@
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public @NonNull NetworkCapabilities setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ public @NonNull NetworkCapabilities setNetworkSpecifier(
+ @NonNull NetworkSpecifier networkSpecifier) {
if (networkSpecifier != null && Long.bitCount(mTransportTypes) != 1) {
throw new IllegalStateException("Must have a single transport specified to use " +
"setNetworkSpecifier");
@@ -964,7 +1190,7 @@
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public @NonNull NetworkCapabilities setTransportInfo(TransportInfo transportInfo) {
+ public @NonNull NetworkCapabilities setTransportInfo(@NonNull TransportInfo transportInfo) {
mTransportInfo = transportInfo;
return this;
}
@@ -973,10 +1199,8 @@
* Gets the optional bearer specific network specifier. May be {@code null} if not set.
*
* @return The optional {@link NetworkSpecifier} specifying the bearer specific network
- * specifier or {@code null}. See {@link #setNetworkSpecifier}.
- * @hide
+ * specifier or {@code null}.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public @Nullable NetworkSpecifier getNetworkSpecifier() {
return mNetworkSpecifier;
}
@@ -1002,7 +1226,7 @@
}
private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
- return mNetworkSpecifier == null || mNetworkSpecifier.satisfiedBy(nc.mNetworkSpecifier)
+ return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
|| nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
}
@@ -1047,7 +1271,6 @@
* @param signalStrength the bearer-specific signal strength.
* @hide
*/
- @UnsupportedAppUsage
public @NonNull NetworkCapabilities setSignalStrength(int signalStrength) {
mSignalStrength = signalStrength;
return this;
@@ -1102,7 +1325,7 @@
* member is null, then the network is not restricted by app UID. If it's an empty list, then
* it means nobody can use it.
* As a special exception, the app managing this network (as identified by its UID stored in
- * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in
+ * mOwnerUid) can always see this network. This is embodied by a special check in
* satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong>
* to the app that manages it as determined by #appliesToUid.
* <p>
@@ -1209,7 +1432,7 @@
* in the passed nc (representing the UIDs that this network is available to).
* <p>
* As a special exception, the UID that created the passed network (as represented by its
- * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN
+ * mOwnerUid field) always satisfies a NetworkRequest requiring it (of LISTEN
* or REQUEST types alike), even if the network does not apply to it. That is so a VPN app
* can see its own network when it listens for it.
* <p>
@@ -1220,7 +1443,7 @@
public boolean satisfiedByUids(@NonNull NetworkCapabilities nc) {
if (null == nc.mUids || null == mUids) return true; // The network satisfies everything.
for (UidRange requiredRange : mUids) {
- if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
+ if (requiredRange.contains(nc.mOwnerUid)) return true;
if (!nc.appliesToUidRange(requiredRange)) {
return false;
}
@@ -1282,7 +1505,9 @@
* Gets the SSID of this network, or null if none or unknown.
* @hide
*/
- public @Nullable String getSSID() {
+ @SystemApi
+ @TestApi
+ public @Nullable String getSsid() {
return mSSID;
}
@@ -1333,6 +1558,8 @@
combineSignalStrength(nc);
combineUids(nc);
combineSSIDs(nc);
+ combineRequestor(nc);
+ combineAdministratorUids(nc);
}
/**
@@ -1352,7 +1579,8 @@
&& satisfiedBySpecifier(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))
- && (onlyImmutable || satisfiedBySSID(nc)));
+ && (onlyImmutable || satisfiedBySSID(nc)))
+ && (onlyImmutable || satisfiedByRequestor(nc));
}
/**
@@ -1436,14 +1664,17 @@
public boolean equals(@Nullable Object obj) {
if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
NetworkCapabilities that = (NetworkCapabilities) obj;
- return (equalsNetCapabilities(that)
+ return equalsNetCapabilities(that)
&& equalsTransportTypes(that)
&& equalsLinkBandwidths(that)
&& equalsSignalStrength(that)
&& equalsSpecifier(that)
&& equalsTransportInfo(that)
&& equalsUids(that)
- && equalsSSID(that));
+ && equalsSSID(that)
+ && equalsPrivateDnsBroken(that)
+ && equalsRequestor(that)
+ && equalsAdministratorUids(that);
}
@Override
@@ -1460,13 +1691,18 @@
+ (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37
- + Objects.hashCode(mTransportInfo) * 41;
+ + Objects.hashCode(mTransportInfo) * 41
+ + Objects.hashCode(mPrivateDnsBroken) * 43
+ + Objects.hashCode(mRequestorUid) * 47
+ + Objects.hashCode(mRequestorPackageName) * 53
+ + Arrays.hashCode(mAdministratorUids) * 59;
}
@Override
public int describeContents() {
return 0;
}
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1479,6 +1715,11 @@
dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids);
dest.writeString(mSSID);
+ dest.writeBoolean(mPrivateDnsBroken);
+ dest.writeIntArray(getAdministratorUids());
+ dest.writeInt(mOwnerUid);
+ dest.writeInt(mRequestorUid);
+ dest.writeString(mRequestorPackageName);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1498,6 +1739,11 @@
netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
+ netCap.mPrivateDnsBroken = in.readBoolean();
+ netCap.setAdministratorUids(in.createIntArray());
+ netCap.mOwnerUid = in.readInt();
+ netCap.mRequestorUid = in.readInt();
+ netCap.mRequestorPackageName = in.readString();
return netCap;
}
@Override
@@ -1547,14 +1793,25 @@
sb.append(" Uids: <").append(mUids).append(">");
}
}
- if (mEstablishingVpnAppUid != INVALID_UID) {
- sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
+ if (mOwnerUid != Process.INVALID_UID) {
+ sb.append(" OwnerUid: ").append(mOwnerUid);
+ }
+
+ if (mAdministratorUids.length == 0) {
+ sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
}
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
+ if (mPrivateDnsBroken) {
+ sb.append(" Private DNS is broken");
+ }
+
+ sb.append(" RequestorUid: ").append(mRequestorUid);
+ sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+
sb.append("]");
return sb.toString();
}
@@ -1563,6 +1820,7 @@
private interface NameOf {
String nameOf(int value);
}
+
/**
* @hide
*/
@@ -1585,7 +1843,7 @@
}
/** @hide */
- public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
+ public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
for (int transport : getTransportTypes()) {
@@ -1655,6 +1913,7 @@
case NET_CAPABILITY_OEM_PAID: return "OEM_PAID";
case NET_CAPABILITY_MCX: return "MCX";
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
+ case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
default: return Integer.toString(capability);
}
}
@@ -1706,4 +1965,469 @@
public boolean isMetered() {
return !hasCapability(NET_CAPABILITY_NOT_METERED);
}
+
+ /**
+ * Check if private dns is broken.
+ *
+ * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken.
+ * @hide
+ */
+ public boolean isPrivateDnsBroken() {
+ return mPrivateDnsBroken;
+ }
+
+ /**
+ * Set mPrivateDnsBroken to true when private dns is broken.
+ *
+ * @param broken the status of private DNS to be set.
+ * @hide
+ */
+ public void setPrivateDnsBroken(boolean broken) {
+ mPrivateDnsBroken = broken;
+ }
+
+ private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
+ return mPrivateDnsBroken == nc.mPrivateDnsBroken;
+ }
+
+ /**
+ * Set the UID of the app making the request.
+ *
+ * For instances of NetworkCapabilities representing a request, sets the
+ * UID of the app making the request. For a network created by the system,
+ * sets the UID of the only app whose requests can match this network.
+ * This can be set to {@link Process#INVALID_UID} if there is no such app,
+ * or if this instance of NetworkCapabilities is about to be sent to a
+ * party that should not learn about this.
+ *
+ * @param uid UID of the app.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setRequestorUid(int uid) {
+ mRequestorUid = uid;
+ return this;
+ }
+
+ /**
+ * Returns the UID of the app making the request.
+ *
+ * For a NetworkRequest being made by an app, contains the app's UID. For a network
+ * created by the system, contains the UID of the only app whose requests can match
+ * this network, or {@link Process#INVALID_UID} if none or if the
+ * caller does not have permission to learn about this.
+ *
+ * @return the uid of the app making the request.
+ * @hide
+ */
+ public int getRequestorUid() {
+ return mRequestorUid;
+ }
+
+ /**
+ * Set the package name of the app making the request.
+ *
+ * For instances of NetworkCapabilities representing a request, sets the
+ * package name of the app making the request. For a network created by the system,
+ * sets the package name of the only app whose requests can match this network.
+ * This can be set to null if there is no such app, or if this instance of
+ * NetworkCapabilities is about to be sent to a party that should not learn about this.
+ *
+ * @param packageName package name of the app.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
+ mRequestorPackageName = packageName;
+ return this;
+ }
+
+ /**
+ * Returns the package name of the app making the request.
+ *
+ * For a NetworkRequest being made by an app, contains the app's package name. For a
+ * network created by the system, contains the package name of the only app whose
+ * requests can match this network, or null if none or if the caller does not have
+ * permission to learn about this.
+ *
+ * @return the package name of the app making the request.
+ * @hide
+ */
+ @Nullable
+ public String getRequestorPackageName() {
+ return mRequestorPackageName;
+ }
+
+ /**
+ * Set the uid and package name of the app causing this network to exist.
+ *
+ * {@see #setRequestorUid} and {@link #setRequestorPackageName}
+ *
+ * @param uid UID of the app.
+ * @param packageName package name of the app.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setRequestorUidAndPackageName(
+ int uid, @NonNull String packageName) {
+ return setRequestorUid(uid).setRequestorPackageName(packageName);
+ }
+
+ /**
+ * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this
+ * capabilities.
+ *
+ * This method is called on the NetworkCapabilities embedded in a request with the
+ * capabilities of an available network. If the available network, sets a specific
+ * requestor (by uid and optionally package name), then this will only match a request from the
+ * same app. If either of the capabilities have an unset uid or package name, then it matches
+ * everything.
+ * <p>
+ * nc is assumed nonnull. Else, NPE.
+ */
+ private boolean satisfiedByRequestor(NetworkCapabilities nc) {
+ // No uid set, matches everything.
+ if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) {
+ return true;
+ }
+ // uids don't match.
+ if (mRequestorUid != nc.mRequestorUid) return false;
+ // No package names set, matches everything
+ if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true;
+ // check for package name match.
+ return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+ }
+
+ /**
+ * Combine requestor info of the capabilities.
+ * <p>
+ * This is only legal if either the requestor info of this object is reset, or both info are
+ * equal.
+ * nc is assumed nonnull.
+ */
+ private void combineRequestor(@NonNull NetworkCapabilities nc) {
+ if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
+ throw new IllegalStateException("Can't combine two uids");
+ }
+ if (mRequestorPackageName != null
+ && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
+ throw new IllegalStateException("Can't combine two package names");
+ }
+ setRequestorUid(nc.mRequestorUid);
+ setRequestorPackageName(nc.mRequestorPackageName);
+ }
+
+ private boolean equalsRequestor(NetworkCapabilities nc) {
+ return mRequestorUid == nc.mRequestorUid
+ && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+ }
+
+ /**
+ * Builder class for NetworkCapabilities.
+ *
+ * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
+ * the built class require holding a signature permission to use - mostly
+ * {@link android.Manifest.permission.NETWORK_FACTORY}, but refer to the specific
+ * description of each setter. As this class lives entirely in app space it does not
+ * enforce these restrictions itself but the system server clears out the relevant
+ * fields when receiving a NetworkCapabilities object from a caller without the
+ * appropriate permission.
+ *
+ * Apps don't use this builder directly. Instead, they use {@link NetworkRequest} via
+ * its builder object.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final class Builder {
+ private final NetworkCapabilities mCaps;
+
+ /**
+ * Creates a new Builder to construct NetworkCapabilities objects.
+ */
+ public Builder() {
+ mCaps = new NetworkCapabilities();
+ }
+
+ /**
+ * Creates a new Builder of NetworkCapabilities from an existing instance.
+ */
+ public Builder(@NonNull final NetworkCapabilities nc) {
+ Objects.requireNonNull(nc);
+ mCaps = new NetworkCapabilities(nc);
+ }
+
+ /**
+ * Adds the given transport type.
+ *
+ * Multiple transports may be added. Note that when searching for a network to satisfy a
+ * request, satisfying any of the transports listed in the request will satisfy the request.
+ * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
+ * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
+ * to be selected. This is logically different than
+ * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+ *
+ * @param transportType the transport type to be added or removed.
+ * @return this builder
+ */
+ @NonNull
+ public Builder addTransportType(@Transport int transportType) {
+ checkValidTransportType(transportType);
+ mCaps.addTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * Removes the given transport type.
+ *
+ * {@see #addTransportType}.
+ *
+ * @param transportType the transport type to be added or removed.
+ * @return this builder
+ */
+ @NonNull
+ public Builder removeTransportType(@Transport int transportType) {
+ checkValidTransportType(transportType);
+ mCaps.removeTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * Adds the given capability.
+ *
+ * @param capability the capability
+ * @return this builder
+ */
+ @NonNull
+ public Builder addCapability(@NetCapability final int capability) {
+ mCaps.setCapability(capability, true);
+ return this;
+ }
+
+ /**
+ * Removes the given capability.
+ *
+ * @param capability the capability
+ * @return this builder
+ */
+ @NonNull
+ public Builder removeCapability(@NetCapability final int capability) {
+ mCaps.setCapability(capability, false);
+ return this;
+ }
+
+ /**
+ * Sets the owner UID.
+ *
+ * The default value is {@link Process#INVALID_UID}. Pass this value to reset.
+ *
+ * Note: for security the system will clear out this field when received from a
+ * non-privileged source.
+ *
+ * @param ownerUid the owner UID
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setOwnerUid(final int ownerUid) {
+ mCaps.setOwnerUid(ownerUid);
+ return this;
+ }
+
+ /**
+ * Sets the list of UIDs that are administrators of this network.
+ *
+ * <p>UIDs included in administratorUids gain administrator privileges over this
+ * Network. Examples of UIDs that should be included in administratorUids are:
+ * <ul>
+ * <li>Carrier apps with privileges for the relevant subscription
+ * <li>Active VPN apps
+ * <li>Other application groups with a particular Network-related role
+ * </ul>
+ *
+ * <p>In general, user-supplied networks (such as WiFi networks) do not have
+ * administrators.
+ *
+ * <p>An app is granted owner privileges over Networks that it supplies. The owner
+ * UID MUST always be included in administratorUids.
+ *
+ * The default value is the empty array. Pass an empty array to reset.
+ *
+ * Note: for security the system will clear out this field when received from a
+ * non-privileged source, such as an app using reflection to call this or
+ * mutate the member in the built object.
+ *
+ * @param administratorUids the UIDs to be set as administrators of this Network.
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setAdministratorUids(@NonNull final int[] administratorUids) {
+ Objects.requireNonNull(administratorUids);
+ mCaps.setAdministratorUids(administratorUids);
+ return this;
+ }
+
+ /**
+ * Sets the upstream bandwidth of the link.
+ *
+ * Sets the upstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * Note that when used to request a network, this specifies the minimum acceptable.
+ * When received as the state of an existing network this specifies the typical
+ * first hop bandwidth expected. This is never measured, but rather is inferred
+ * from technology type and other link parameters. It could be used to differentiate
+ * between very slow 1xRTT cellular links and other faster networks or even between
+ * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+ * fast backhauls and slow backhauls.
+ *
+ * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setLinkUpstreamBandwidthKbps(final int upKbps) {
+ mCaps.setLinkUpstreamBandwidthKbps(upKbps);
+ return this;
+ }
+
+ /**
+ * Sets the downstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * Note that when used to request a network, this specifies the minimum acceptable.
+ * When received as the state of an existing network this specifies the typical
+ * first hop bandwidth expected. This is never measured, but rather is inferred
+ * from technology type and other link parameters. It could be used to differentiate
+ * between very slow 1xRTT cellular links and other faster networks or even between
+ * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+ * fast backhauls and slow backhauls.
+ *
+ * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setLinkDownstreamBandwidthKbps(final int downKbps) {
+ mCaps.setLinkDownstreamBandwidthKbps(downKbps);
+ return this;
+ }
+
+ /**
+ * Sets the optional bearer specific network specifier.
+ * This has no meaning if a single transport is also not specified, so calling
+ * this without a single transport set will generate an exception, as will
+ * subsequently adding or removing transports after this is set.
+ * </p>
+ *
+ * @param specifier a concrete, parcelable framework class that extends NetworkSpecifier,
+ * or null to clear it.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setNetworkSpecifier(@Nullable final NetworkSpecifier specifier) {
+ mCaps.setNetworkSpecifier(specifier);
+ return this;
+ }
+
+ /**
+ * Sets the optional transport specific information.
+ *
+ * @param info A concrete, parcelable framework class that extends {@link TransportInfo},
+ * or null to clear it.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setTransportInfo(@Nullable final TransportInfo info) {
+ mCaps.setTransportInfo(info);
+ return this;
+ }
+
+ /**
+ * Sets the signal strength. This is a signed integer, with higher values indicating a
+ * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the
+ * same RSSI units reported by wifi code.
+ * <p>
+ * Note that when used to register a network callback, this specifies the minimum
+ * acceptable signal strength. When received as the state of an existing network it
+ * specifies the current value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means
+ * no value when received and has no effect when requesting a callback.
+ *
+ * Note: for security the system will throw if it receives a NetworkRequest where
+ * the underlying NetworkCapabilities has this member set from a source that does
+ * not hold the {@link android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP}
+ * permission. Apps with this permission can use this indirectly through
+ * {@link android.net.NetworkRequest}.
+ *
+ * @param signalStrength the bearer-specific signal strength.
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP)
+ public Builder setSignalStrength(final int signalStrength) {
+ mCaps.setSignalStrength(signalStrength);
+ return this;
+ }
+
+ /**
+ * Sets the SSID of this network.
+ *
+ * Note: for security the system will clear out this field when received from a
+ * non-privileged source, like an app using reflection to set this.
+ *
+ * @param ssid the SSID, or null to clear it.
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setSsid(@Nullable final String ssid) {
+ mCaps.setSSID(ssid);
+ return this;
+ }
+
+ /**
+ * Set the uid of the app causing this network to exist.
+ *
+ * Note: for security the system will clear out this field when received from a
+ * non-privileged source.
+ *
+ * @param uid UID of the app.
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setRequestorUid(final int uid) {
+ mCaps.setRequestorUid(uid);
+ return this;
+ }
+
+ /**
+ * Set the package name of the app causing this network to exist.
+ *
+ * Note: for security the system will clear out this field when received from a
+ * non-privileged source.
+ *
+ * @param packageName package name of the app, or null to clear it.
+ * @return this builder
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public Builder setRequestorPackageName(@Nullable final String packageName) {
+ mCaps.setRequestorPackageName(packageName);
+ return this;
+ }
+
+ /**
+ * Builds the instance of the capabilities.
+ *
+ * @return the built instance of NetworkCapabilities.
+ */
+ @NonNull
+ public NetworkCapabilities build() {
+ if (mCaps.getOwnerUid() != Process.INVALID_UID) {
+ if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+ throw new IllegalStateException("The owner UID must be included in "
+ + " administrator UIDs.");
+ }
+ }
+ return new NetworkCapabilities(mCaps);
+ }
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 92f105f..08fe159 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -17,9 +17,11 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
import com.android.internal.annotations.VisibleForTesting;
@@ -150,10 +152,19 @@
private boolean mIsRoaming;
/**
- * @hide
+ * Create a new instance of NetworkInfo.
+ *
+ * This may be useful for apps to write unit tests.
+ *
+ * @param type the legacy type of the network, as one of the ConnectivityManager.TYPE_*
+ * constants.
+ * @param subtype the subtype if applicable, as one of the TelephonyManager.NETWORK_TYPE_*
+ * constants.
+ * @param typeName a human-readable string for the network type, or an empty string or null.
+ * @param subtypeName a human-readable string for the subtype, or an empty string or null.
*/
- @UnsupportedAppUsage
- public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
+ public NetworkInfo(int type, @NetworkType int subtype,
+ @Nullable String typeName, @Nullable String subtypeName) {
if (!ConnectivityManager.isNetworkTypeValid(type)
&& type != ConnectivityManager.TYPE_NONE) {
throw new IllegalArgumentException("Invalid network type: " + type);
@@ -462,17 +473,19 @@
/**
* Sets the fine-grained state of the network.
+ *
+ * This is only useful for testing.
+ *
* @param detailedState the {@link DetailedState}.
* @param reason a {@code String} indicating the reason for the state change,
* if one was supplied. May be {@code null}.
* @param extraInfo an optional {@code String} providing addditional network state
* information passed up from the lower networking layers.
* @deprecated Use {@link NetworkCapabilities} instead.
- * @hide
*/
@Deprecated
- @UnsupportedAppUsage
- public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
+ public void setDetailedState(@NonNull DetailedState detailedState, @Nullable String reason,
+ @Nullable String extraInfo) {
synchronized (this) {
this.mDetailedState = detailedState;
this.mState = stateMap.get(detailedState);
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
deleted file mode 100644
index 9ba3bd9..0000000
--- a/core/java/android/net/NetworkMisc.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2014 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.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A grab-bag of information (metadata, policies, properties, etc) about a
- * {@link Network}. Since this contains PII, it should not be sent outside the
- * system.
- *
- * @hide
- */
-public class NetworkMisc implements Parcelable {
-
- /**
- * If the {@link Network} is a VPN, whether apps are allowed to bypass the
- * VPN. This is set by a {@link VpnService} and used by
- * {@link ConnectivityManager} when creating a VPN.
- */
- public boolean allowBypass;
-
- /**
- * Set if the network was manually/explicitly connected to by the user either from settings
- * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
- * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
- * connect to a particular access point is also explicit, though this may change in the future
- * as we want apps to use the multinetwork apis.
- */
- public boolean explicitlySelected;
-
- /**
- * Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
- * appropriate value based on previous user choice.
- */
- public boolean acceptUnvalidated;
-
- /**
- * Whether the user explicitly set that this network should be validated even if presence of
- * only partial internet connectivity.
- */
- public boolean acceptPartialConnectivity;
-
- /**
- * Set to avoid surfacing the "Sign in to network" notification.
- * if carrier receivers/apps are registered to handle the carrier-specific provisioning
- * procedure, a carrier specific provisioning notification will be placed.
- * only one notification should be displayed. This field is set based on
- * which notification should be used for provisioning.
- */
- public boolean provisioningNotificationDisabled;
-
- /**
- * For mobile networks, this is the subscriber ID (such as IMSI).
- */
- public String subscriberId;
-
- /**
- * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
- * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
- */
- public boolean skip464xlat;
-
- public NetworkMisc() {
- }
-
- public NetworkMisc(NetworkMisc nm) {
- if (nm != null) {
- allowBypass = nm.allowBypass;
- explicitlySelected = nm.explicitlySelected;
- acceptUnvalidated = nm.acceptUnvalidated;
- subscriberId = nm.subscriberId;
- provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
- skip464xlat = nm.skip464xlat;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(allowBypass ? 1 : 0);
- out.writeInt(explicitlySelected ? 1 : 0);
- out.writeInt(acceptUnvalidated ? 1 : 0);
- out.writeString(subscriberId);
- out.writeInt(provisioningNotificationDisabled ? 1 : 0);
- out.writeInt(skip464xlat ? 1 : 0);
- }
-
- public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
- @Override
- public NetworkMisc createFromParcel(Parcel in) {
- NetworkMisc networkMisc = new NetworkMisc();
- networkMisc.allowBypass = in.readInt() != 0;
- networkMisc.explicitlySelected = in.readInt() != 0;
- networkMisc.acceptUnvalidated = in.readInt() != 0;
- networkMisc.subscriberId = in.readString();
- networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
- networkMisc.skip464xlat = in.readInt() != 0;
- return networkMisc;
- }
-
- @Override
- public NetworkMisc[] newArray(int size) {
- return new NetworkMisc[size];
- }
- };
-}
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
new file mode 100644
index 0000000..75086cf
--- /dev/null
+++ b/core/java/android/net/NetworkProvider.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+/**
+ * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device
+ * to networks and makes them available to to the core network stack by creating
+ * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted
+ * with via networking APIs such as {@link ConnectivityManager}.
+ *
+ * Subclasses should implement {@link #onNetworkRequested} and {@link #onNetworkRequestWithdrawn}
+ * to receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the
+ * best (highest-scoring) network for any request is generally not used by the system, and torn
+ * down.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkProvider {
+ /**
+ * {@code providerId} value that indicates the absence of a provider. It is the providerId of
+ * any NetworkProvider that is not currently registered, and of any NetworkRequest that is not
+ * currently being satisfied by a network.
+ */
+ public static final int ID_NONE = -1;
+
+ /**
+ * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any
+ * provider, so they use this constant for clarity instead of NONE.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int ID_VPN = -2;
+
+ /**
+ * The first providerId value that will be allocated.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int FIRST_PROVIDER_ID = 1;
+
+ /** @hide only used by ConnectivityService */
+ public static final int CMD_REQUEST_NETWORK = 1;
+ /** @hide only used by ConnectivityService */
+ public static final int CMD_CANCEL_REQUEST = 2;
+
+ private final Messenger mMessenger;
+ private final String mName;
+ private final ConnectivityManager mCm;
+
+ private int mProviderId = ID_NONE;
+
+ /**
+ * Constructs a new NetworkProvider.
+ *
+ * @param looper the Looper on which to run {@link #onNetworkRequested} and
+ * {@link #onNetworkRequestWithdrawn}.
+ * @param name the name of the listener, used only for debugging.
+ *
+ * @hide
+ */
+ @SystemApi
+ public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) {
+ mCm = ConnectivityManager.from(context);
+
+ Handler handler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message m) {
+ switch (m.what) {
+ case CMD_REQUEST_NETWORK:
+ onNetworkRequested((NetworkRequest) m.obj, m.arg1, m.arg2);
+ break;
+ case CMD_CANCEL_REQUEST:
+ onNetworkRequestWithdrawn((NetworkRequest) m.obj);
+ break;
+ default:
+ Log.e(mName, "Unhandled message: " + m.what);
+ }
+ }
+ };
+ mMessenger = new Messenger(handler);
+ mName = name;
+ }
+
+ // TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+ /** @hide */
+ public @Nullable Messenger getMessenger() {
+ return mMessenger;
+ }
+
+ /** @hide */
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the ID of this provider. This is known only once the provider is registered via
+ * {@link ConnectivityManager#registerNetworkProvider()}, otherwise the ID is {@link #ID_NONE}.
+ * This ID must be used when registering any {@link NetworkAgent}s.
+ */
+ public int getProviderId() {
+ return mProviderId;
+ }
+
+ /** @hide */
+ public void setProviderId(int providerId) {
+ mProviderId = providerId;
+ }
+
+ /**
+ * Called when a NetworkRequest is received. The request may be a new request or an existing
+ * request with a different score.
+ *
+ * @param request the NetworkRequest being received
+ * @param score the score of the network currently satisfying the request, or 0 if none.
+ * @param providerId the ID of the provider that created the network currently satisfying this
+ * request, or {@link #ID_NONE} if none.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onNetworkRequested(@NonNull NetworkRequest request,
+ @IntRange(from = 0, to = 99) int score, int providerId) {}
+
+ /**
+ * Called when a NetworkRequest is withdrawn.
+ * @hide
+ */
+ @SystemApi
+ public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {}
+
+ /**
+ * Asserts that no provider will ever be able to satisfy the specified request. The provider
+ * must only call this method if it knows that it is the only provider on the system capable of
+ * satisfying this request, and that the request cannot be satisfied. The application filing the
+ * request will receive an {@link NetworkCallback#onUnavailable()} callback.
+ *
+ * @param request the request that permanently cannot be fulfilled
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
+ mCm.declareNetworkRequestUnfulfillable(request);
+ }
+}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 4270740..473e6c5 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,9 +17,10 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkCapabilities.Transport;
import android.os.Build;
@@ -246,9 +247,8 @@
* removing even the capabilities that are set by default when the object is constructed.
*
* @return The builder to facilitate chaining.
- * @hide
*/
- @UnsupportedAppUsage
+ @NonNull
public Builder clearCapabilities() {
mNetworkCapabilities.clearAll();
return this;
@@ -300,22 +300,34 @@
* this without a single transport set will generate an exception, as will
* subsequently adding or removing transports after this is set.
* </p>
- * The interpretation of this {@code String} is bearer specific and bearers that use
- * it should document their particulars. For example, Bluetooth may use some sort of
- * device id while WiFi could used ssid and/or bssid. Cellular may use carrier spn.
+ * If the {@code networkSpecifier} is provided, it shall be interpreted as follows:
+ * <ul>
+ * <li>If the specifier can be parsed as an integer, it will be treated as a
+ * {@link android.net TelephonyNetworkSpecifier}, and the provided integer will be
+ * interpreted as a SubscriptionId.
+ * <li>If the value is an ethernet interface name, it will be treated as such.
+ * <li>For all other cases, the behavior is undefined.
+ * </ul>
*
- * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
- * specific network specifier where the bearer has a choice of
- * networks.
+ * @param networkSpecifier A {@code String} of either a SubscriptionId in cellular
+ * network request or an ethernet interface name in ethernet
+ * network request.
+ *
+ * @deprecated Use {@link #setNetworkSpecifier(NetworkSpecifier)} instead.
*/
+ @Deprecated
public Builder setNetworkSpecifier(String networkSpecifier) {
- /*
- * A StringNetworkSpecifier does not accept null or empty ("") strings. When network
- * specifiers were strings a null string and an empty string were considered equivalent.
- * Hence no meaning is attached to a null or empty ("") string.
- */
- return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
- : new StringNetworkSpecifier(networkSpecifier));
+ try {
+ int subId = Integer.parseInt(networkSpecifier);
+ return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(subId).build());
+ } catch (NumberFormatException nfe) {
+ // A StringNetworkSpecifier does not accept null or empty ("") strings. When network
+ // specifiers were strings a null string and an empty string were considered
+ // equivalent. Hence no meaning is attached to a null or empty ("") string.
+ return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
+ : new StringNetworkSpecifier(networkSpecifier));
+ }
}
/**
@@ -368,6 +380,7 @@
dest.writeInt(requestId);
dest.writeString(type.name());
}
+
public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR =
new Creator<NetworkRequest>() {
public NetworkRequest createFromParcel(Parcel in) {
@@ -455,12 +468,56 @@
}
/**
+ * Returns true if and only if the capabilities requested in this NetworkRequest are satisfied
+ * by the provided {@link NetworkCapabilities}.
+ *
+ * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
+ * satisfy any request.
+ */
+ public boolean canBeSatisfiedBy(@Nullable NetworkCapabilities nc) {
+ return networkCapabilities.satisfiedByNetworkCapabilities(nc);
+ }
+
+ /**
* @see Builder#addTransportType(int)
*/
public boolean hasTransport(@Transport int transportType) {
return networkCapabilities.hasTransport(transportType);
}
+ /**
+ * @see Builder#setNetworkSpecifier(NetworkSpecifier)
+ */
+ @Nullable
+ public NetworkSpecifier getNetworkSpecifier() {
+ return networkCapabilities.getNetworkSpecifier();
+ }
+
+ /**
+ * @return the uid of the app making the request.
+ *
+ * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was
+ * not obtained from {@link ConnectivityManager}.
+ * @hide
+ */
+ @SystemApi
+ public int getRequestorUid() {
+ return networkCapabilities.getRequestorUid();
+ }
+
+ /**
+ * @return the package name of the app making the request.
+ *
+ * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+ * from {@link ConnectivityManager}.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public String getRequestorPackageName() {
+ return networkCapabilities.getRequestorPackageName();
+ }
+
public String toString() {
return "NetworkRequest [ " + type + " id=" + requestId +
(legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
@@ -485,13 +542,13 @@
}
/** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(NetworkRequestProto.TYPE, typeToProtoEnum(type));
proto.write(NetworkRequestProto.REQUEST_ID, requestId);
proto.write(NetworkRequestProto.LEGACY_TYPE, legacyType);
- networkCapabilities.writeToProto(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
+ networkCapabilities.dumpDebug(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
proto.end(token);
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 292cf50..e449615 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,7 +16,7 @@
package android.net;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d0f54b4..97a7ecc 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -20,18 +20,18 @@
import static android.system.OsConstants.AF_INET6;
import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
-import android.net.shared.Inet4AddressUtils;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import android.util.Pair;
+import com.android.net.module.util.Inet4AddressUtils;
+
import java.io.FileDescriptor;
import java.math.BigInteger;
import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
@@ -61,13 +61,6 @@
public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
/**
- * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
- * @param fd the socket's {@link FileDescriptor}.
- * @param ifIndex the interface index.
- */
- public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException;
-
- /**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network#getSocketFactory}) will be bound to this network. Note that if this
@@ -163,6 +156,14 @@
public static native Network getDnsNetwork() throws ErrnoException;
/**
+ * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
+ *
+ * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
+ * and DNS lookups.
+ */
+ public static native void setAllowNetworkingForProcess(boolean allowNetworking);
+
+ /**
* Get the tcp repair window associated with the {@code fd}.
*
* @param fd the tcp socket's {@link FileDescriptor}.
@@ -320,15 +321,6 @@
}
/**
- * Check if IP address type is consistent between two InetAddress.
- * @return true if both are the same type. False otherwise.
- */
- public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
- return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
- ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
- }
-
- /**
* Convert a 32 char hex string into a Inet6Address.
* throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
* made into an Inet6Address
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 807c467..ffe9ae9 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -16,8 +16,9 @@
package android.net;
-
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -89,6 +90,15 @@
}
/**
+ * Construct a {@link ProxyInfo} object that will download and run the PAC script at the
+ * specified URL and port.
+ */
+ @NonNull
+ public static ProxyInfo buildPacProxy(@NonNull Uri pacUrl, int port) {
+ return new ProxyInfo(pacUrl, port);
+ }
+
+ /**
* Create a ProxyProperties that points at a HTTP Proxy.
* @hide
*/
@@ -105,7 +115,7 @@
* Create a ProxyProperties that points at a PAC URL.
* @hide
*/
- public ProxyInfo(Uri pacFileUrl) {
+ public ProxyInfo(@NonNull Uri pacFileUrl) {
mHost = LOCAL_HOST;
mPort = LOCAL_PORT;
mExclusionList = LOCAL_EXCL_LIST;
@@ -132,7 +142,7 @@
* Only used in PacManager after Local Proxy is bound.
* @hide
*/
- public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
+ public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
mHost = LOCAL_HOST;
mPort = localProxyPort;
mExclusionList = LOCAL_EXCL_LIST;
@@ -159,11 +169,10 @@
mPacFileUrl = Uri.EMPTY;
}
- // copy constructor instead of clone
/**
- * @hide
+ * A copy constructor to hold proxy properties.
*/
- public ProxyInfo(ProxyInfo source) {
+ public ProxyInfo(@Nullable ProxyInfo source) {
if (source != null) {
mHost = source.getHost();
mPort = source.getPort();
@@ -226,12 +235,13 @@
* comma separated
* @hide
*/
+ @Nullable
public String getExclusionListAsString() {
return mExclusionList;
}
/**
- * @hide
+ * Return true if the pattern of proxy is valid, otherwise return false.
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 52d3fc4..9876076 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -21,7 +21,8 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,6 +105,11 @@
*/
private final int mType;
+ /**
+ * The maximum transmission unit size for this route.
+ */
+ private final int mMtu;
+
// Derived data members.
// TODO: remove these.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -132,6 +138,31 @@
@TestApi
public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
@Nullable String iface, @RouteType int type) {
+ this(destination, gateway, iface, type, 0);
+ }
+
+ /**
+ * Constructs a RouteInfo object.
+ *
+ * If destination is null, then gateway must be specified and the
+ * constructed route is either the IPv4 default route <code>0.0.0.0</code>
+ * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
+ * route <code>::/0</code> if gateway is an instance of
+ * {@link Inet6Address}.
+ * <p>
+ * destination and gateway may not both be null.
+ *
+ * @param destination the destination prefix
+ * @param gateway the IP address to route packets through
+ * @param iface the interface name to send packets on
+ * @param type the type of this route
+ * @param mtu the maximum transmission unit size for this route
+ *
+ * @hide
+ */
+ @SystemApi
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface, @RouteType int type, int mtu) {
switch (type) {
case RTN_UNICAST:
case RTN_UNREACHABLE:
@@ -161,7 +192,7 @@
} else {
// no destination, no gateway. invalid.
throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
- destination);
+ destination);
}
}
// TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and
@@ -176,10 +207,10 @@
}
mHasGateway = (!gateway.isAnyLocalAddress());
- if ((destination.getAddress() instanceof Inet4Address &&
- (gateway instanceof Inet4Address == false)) ||
- (destination.getAddress() instanceof Inet6Address &&
- (gateway instanceof Inet6Address == false))) {
+ if ((destination.getAddress() instanceof Inet4Address
+ && !(gateway instanceof Inet4Address))
+ || (destination.getAddress() instanceof Inet6Address
+ && !(gateway instanceof Inet6Address))) {
throw new IllegalArgumentException("address family mismatch in RouteInfo constructor");
}
mDestination = destination; // IpPrefix objects are immutable.
@@ -187,6 +218,7 @@
mInterface = iface; // Strings are immutable.
mType = type;
mIsHost = isHost();
+ mMtu = mtu;
}
/**
@@ -373,6 +405,17 @@
}
/**
+ * Retrieves the MTU size for this route.
+ *
+ * @return The MTU size, or 0 if it has not been set.
+ * @hide
+ */
+ @SystemApi
+ public int getMtu() {
+ return mMtu;
+ }
+
+ /**
* Indicates if this route is a default route (ie, has no destination specified).
*
* @return {@code true} if the destination has a prefix length of 0.
@@ -382,6 +425,16 @@
}
/**
+ * Indicates if this route is an unreachable default route.
+ *
+ * @return {@code true} if it's an unreachable route with prefix length of 0.
+ * @hide
+ */
+ private boolean isUnreachableDefaultRoute() {
+ return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0;
+ }
+
+ /**
* Indicates if this route is an IPv4 default route.
* @hide
*/
@@ -390,6 +443,14 @@
}
/**
+ * Indicates if this route is an IPv4 unreachable default route.
+ * @hide
+ */
+ public boolean isIPv4UnreachableDefault() {
+ return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
+ }
+
+ /**
* Indicates if this route is an IPv6 default route.
* @hide
*/
@@ -398,6 +459,14 @@
}
/**
+ * Indicates if this route is an IPv6 unreachable default route.
+ * @hide
+ */
+ public boolean isIPv6UnreachableDefault() {
+ return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
+ }
+
+ /**
* Indicates if this route is a host route (ie, matches only a single host address).
*
* @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
@@ -441,21 +510,7 @@
@UnsupportedAppUsage
@Nullable
public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
- if ((routes == null) || (dest == null)) return null;
-
- RouteInfo bestRoute = null;
- // pick a longest prefix match under same address type
- for (RouteInfo route : routes) {
- if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
- if ((bestRoute != null) &&
- (bestRoute.mDestination.getPrefixLength() >=
- route.mDestination.getPrefixLength())) {
- continue;
- }
- if (route.matches(dest)) bestRoute = route;
- }
- }
- return bestRoute;
+ return NetUtils.selectBestRoute(routes, dest);
}
/**
@@ -476,6 +531,7 @@
val += " unknown type " + mType;
}
}
+ val += " mtu " + mMtu;
return val;
}
@@ -493,7 +549,61 @@
return Objects.equals(mDestination, target.getDestination()) &&
Objects.equals(mGateway, target.getGateway()) &&
Objects.equals(mInterface, target.getInterface()) &&
- mType == target.getType();
+ mType == target.getType() && mMtu == target.getMtu();
+ }
+
+ /**
+ * A helper class that contains the destination, the gateway and the interface in a
+ * {@code RouteInfo}, used by {@link ConnectivityService#updateRoutes} or
+ * {@link LinkProperties#addRoute} to calculate the list to be updated.
+ * {@code RouteInfo} objects with different interfaces are treated as different routes because
+ * *usually* on Android different interfaces use different routing tables, and moving a route
+ * to a new routing table never constitutes an update, but is always a remove and an add.
+ *
+ * @hide
+ */
+ public static class RouteKey {
+ @NonNull private final IpPrefix mDestination;
+ @Nullable private final InetAddress mGateway;
+ @Nullable private final String mInterface;
+
+ RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
+ mDestination = destination;
+ mGateway = gateway;
+ mInterface = iface;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof RouteKey)) {
+ return false;
+ }
+ RouteKey p = (RouteKey) o;
+ // No need to do anything special for scoped addresses. Inet6Address#equals does not
+ // consider the scope ID, but the netd route IPCs (e.g., INetd#networkAddRouteParcel)
+ // and the kernel ignore scoped addresses both in the prefix and in the nexthop and only
+ // look at RTA_OIF.
+ return Objects.equals(p.mDestination, mDestination)
+ && Objects.equals(p.mGateway, mGateway)
+ && Objects.equals(p.mInterface, mInterface);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDestination, mGateway, mInterface);
+ }
+ }
+
+ /**
+ * Get {@code RouteKey} of this {@code RouteInfo}.
+ * @return a {@code RouteKey} object.
+ *
+ * @hide
+ */
+ @NonNull
+ public RouteKey getRouteKey() {
+ return new RouteKey(mDestination, mGateway, mInterface);
}
/**
@@ -503,7 +613,7 @@
return (mDestination.hashCode() * 41)
+ (mGateway == null ? 0 :mGateway.hashCode() * 47)
+ (mInterface == null ? 0 :mInterface.hashCode() * 67)
- + (mType * 71);
+ + (mType * 71) + (mMtu * 89);
}
/**
@@ -522,6 +632,7 @@
dest.writeByteArray(gatewayBytes);
dest.writeString(mInterface);
dest.writeInt(mType);
+ dest.writeInt(mMtu);
}
/**
@@ -540,8 +651,9 @@
String iface = in.readString();
int type = in.readInt();
+ int mtu = in.readInt();
- return new RouteInfo(dest, gateway, iface, type);
+ return new RouteInfo(dest, gateway, iface, type, mtu);
}
public RouteInfo[] newArray(int size) {
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index ec73866..46aef10 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -53,7 +54,11 @@
public abstract class SocketKeepalive implements AutoCloseable {
static final String TAG = "SocketKeepalive";
- /** @hide */
+ /**
+ * No errors.
+ * @hide
+ */
+ @SystemApi
public static final int SUCCESS = 0;
/** @hide */
@@ -104,6 +109,17 @@
})
public @interface ErrorCode {}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ SUCCESS,
+ ERROR_INVALID_LENGTH,
+ ERROR_UNSUPPORTED,
+ ERROR_INSUFFICIENT_RESOURCES,
+ ERROR_HARDWARE_UNSUPPORTED
+ })
+ public @interface KeepaliveEvent {}
+
/**
* The minimum interval in seconds between keepalive packet transmissions.
*
@@ -147,17 +163,6 @@
}
}
- /**
- * This packet is invalid.
- * See the error code for details.
- * @hide
- */
- public static class InvalidPacketException extends ErrorCodeException {
- public InvalidPacketException(final int error) {
- super(error);
- }
- }
-
@NonNull final IConnectivityManager mService;
@NonNull final Network mNetwork;
@NonNull final ParcelFileDescriptor mPfd;
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 050fcfa..a973455 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -20,11 +20,13 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
-import android.net.shared.InetAddressUtils;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+import com.android.net.module.util.InetAddressUtils;
+
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
@@ -113,8 +115,8 @@
}
/**
- * Get a {@link String} listing in priority order of the comma separated domains to search when
- * resolving host names on the link.
+ * Get a {@link String} containing the comma separated domains to search when resolving host
+ * names on this link, in priority order.
*/
public @Nullable String getDomains() {
return domains;
@@ -152,6 +154,7 @@
* @return The {@link Builder} for chaining.
*/
public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
+ Preconditions.checkNotNull(dnsServers);
mDnsServers = dnsServers;
return this;
}
@@ -175,8 +178,10 @@
final StaticIpConfiguration config = new StaticIpConfiguration();
config.ipAddress = mIpAddress;
config.gateway = mGateway;
- for (InetAddress server : mDnsServers) {
- config.dnsServers.add(server);
+ if (mDnsServers != null) {
+ for (InetAddress server : mDnsServers) {
+ config.dnsServers.add(server);
+ }
}
config.domains = mDomains;
return config;
@@ -236,6 +241,7 @@
return lp;
}
+ @NonNull
@Override
public String toString() {
StringBuffer str = new StringBuffer();
@@ -267,7 +273,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticIpConfiguration)) return false;
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index e274005..a0a563b 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -16,6 +16,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.IBinder;
import android.os.RemoteException;
@@ -29,6 +30,18 @@
*/
@TestApi
public class TestNetworkManager {
+ /**
+ * Prefix for tun interfaces created by this class.
+ * @hide
+ */
+ public static final String TEST_TUN_PREFIX = "testtun";
+
+ /**
+ * Prefix for tap interfaces created by this class.
+ * @hide
+ */
+ public static final String TEST_TAP_PREFIX = "testtap";
+
@NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
@NonNull private final ITestNetworkManager mService;
@@ -53,6 +66,35 @@
}
}
+ private void setupTestNetwork(
+ @NonNull String iface,
+ @Nullable LinkProperties lp,
+ boolean isMetered,
+ @NonNull int[] administratorUids,
+ @NonNull IBinder binder) {
+ try {
+ mService.setupTestNetwork(iface, lp, isMetered, administratorUids, binder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets up a capability-limited, testing-only network for a given interface
+ *
+ * @param lp The LinkProperties for the TestNetworkService to use for this test network. Note
+ * that the interface name and link addresses will be overwritten, and the passed-in values
+ * discarded.
+ * @param isMetered Whether or not the network should be considered metered.
+ * @param binder A binder object guarding the lifecycle of this test network.
+ * @hide
+ */
+ public void setupTestNetwork(
+ @NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
+ Preconditions.checkNotNull(lp, "Invalid LinkProperties");
+ setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
+ }
+
/**
* Sets up a capability-limited, testing-only network for a given interface
*
@@ -62,11 +104,21 @@
*/
@TestApi
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
- try {
- mService.setupTestNetwork(iface, binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setupTestNetwork(iface, null, true, new int[0], binder);
+ }
+
+ /**
+ * Sets up a capability-limited, testing-only network for a given interface with the given
+ * administrator UIDs.
+ *
+ * @param iface the name of the interface to be used for the Network LinkProperties.
+ * @param administratorUids The administrator UIDs to be used for the test-only network
+ * @param binder A binder object guarding the lifecycle of this test network.
+ * @hide
+ */
+ public void setupTestNetwork(
+ @NonNull String iface, @NonNull int[] administratorUids, @NonNull IBinder binder) {
+ setupTestNetwork(iface, null, true, administratorUids, binder);
}
/**
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index 4dd2ace..b1de74e 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -17,6 +17,7 @@
package android.net.apf;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.res.Resources;
@@ -91,6 +92,7 @@
}
};
+ @NonNull
@Override
public String toString() {
return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
@@ -98,7 +100,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ApfCapabilities)) return false;
final ApfCapabilities other = (ApfCapabilities) obj;
return apfVersionSupported == other.apfVersionSupported
diff --git a/core/java/android/net/util/DnsUtils.java b/core/java/android/net/util/DnsUtils.java
index e6abd50..7908353 100644
--- a/core/java/android/net/util/DnsUtils.java
+++ b/core/java/android/net/util/DnsUtils.java
@@ -141,14 +141,17 @@
*/
public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network,
@NonNull List<InetAddress> answers) {
- List<SortableAddress> sortableAnswerList = new ArrayList<>();
- answers.forEach(addr -> sortableAnswerList.add(
- new SortableAddress(addr, findSrcAddress(network, addr))));
+ final ArrayList<SortableAddress> sortableAnswerList = new ArrayList<>();
+ for (InetAddress addr : answers) {
+ sortableAnswerList.add(new SortableAddress(addr, findSrcAddress(network, addr)));
+ }
Collections.sort(sortableAnswerList, sRfc6724Comparator);
final List<InetAddress> sortedAnswers = new ArrayList<>();
- sortableAnswerList.forEach(ans -> sortedAnswers.add(ans.address));
+ for (SortableAddress ans : sortableAnswerList) {
+ sortedAnswers.add(ans.address);
+ }
return sortedAnswers;
}
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index f7e494d..aa0f622 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -64,7 +64,7 @@
private final Context mContext;
private final Handler mHandler;
- private final Runnable mReevaluateRunnable;
+ private final Runnable mAvoidBadWifiCallback;
private final List<Uri> mSettingsUris;
private final ContentResolver mResolver;
private final SettingObserver mSettingObserver;
@@ -81,12 +81,7 @@
public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
mContext = ctx;
mHandler = handler;
- mReevaluateRunnable = () -> {
- if (updateAvoidBadWifi() && avoidBadWifiCallback != null) {
- avoidBadWifiCallback.run();
- }
- updateMeteredMultipathPreference();
- };
+ mAvoidBadWifiCallback = avoidBadWifiCallback;
mSettingsUris = Arrays.asList(
Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
@@ -95,15 +90,16 @@
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- reevaluate();
+ reevaluateInternal();
}
};
- TelephonyManager.from(ctx).listen(new PhoneStateListener() {
+ ctx.getSystemService(TelephonyManager.class).listen(
+ new PhoneStateListener(handler.getLooper()) {
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
mActiveSubId = subId;
- reevaluate();
+ reevaluateInternal();
}
}, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
@@ -119,7 +115,7 @@
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
mContext.registerReceiverAsUser(
- mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null);
+ mBroadcastReceiver, UserHandle.ALL, intentFilter, null, mHandler);
reevaluate();
}
@@ -164,7 +160,17 @@
@VisibleForTesting
public void reevaluate() {
- mHandler.post(mReevaluateRunnable);
+ mHandler.post(this::reevaluateInternal);
+ }
+
+ /**
+ * Reevaluate the settings. Must be called on the handler thread.
+ */
+ private void reevaluateInternal() {
+ if (updateAvoidBadWifi() && mAvoidBadWifiCallback != null) {
+ mAvoidBadWifiCallback.run();
+ }
+ updateMeteredMultipathPreference();
}
public boolean updateAvoidBadWifi() {
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 08aa1d9..d4805ac 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -24,17 +24,16 @@
#include <linux/tcp.h>
#include <net/if.h>
#include <netinet/ether.h>
-#include <netinet/icmp6.h>
#include <netinet/ip.h>
-#include <netinet/ip6.h>
#include <netinet/udp.h>
+#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <android_runtime/AndroidRuntime.h>
#include <cutils/properties.h>
-#include <utils/misc.h>
-#include <utils/Log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
#include "NetdClient.h"
#include "core_jni_helpers.h"
@@ -102,98 +101,6 @@
}
}
-static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
- jint ifIndex)
-{
- static const int kLinkLocalHopLimit = 255;
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
-
- // Set an ICMPv6 filter that only passes Router Solicitations.
- struct icmp6_filter rs_only;
- ICMP6_FILTER_SETBLOCKALL(&rs_only);
- ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
- socklen_t len = sizeof(rs_only);
- if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(ICMP6_FILTER): %s", strerror(errno));
- return;
- }
-
- // Most/all of the rest of these options can be set via Java code, but
- // because we're here on account of setting an icmp6_filter go ahead
- // and do it all natively for now.
- //
- // TODO: Consider moving these out to Java.
-
- // Set the multicast hoplimit to 255 (link-local only).
- int hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Set the unicast hoplimit to 255 (link-local only).
- hops = kLinkLocalHopLimit;
- len = sizeof(hops);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
- return;
- }
-
- // Explicitly disable multicast loopback.
- int off = 0;
- len = sizeof(off);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
- return;
- }
-
- // Specify the IPv6 interface to use for outbound multicast.
- len = sizeof(ifIndex);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
- return;
- }
-
- // Additional options to be considered:
- // - IPV6_TCLASS
- // - IPV6_RECVPKTINFO
- // - IPV6_RECVHOPLIMIT
-
- // Bind to [::].
- const struct sockaddr_in6 sin6 = {
- .sin6_family = AF_INET6,
- .sin6_port = 0,
- .sin6_flowinfo = 0,
- .sin6_addr = IN6ADDR_ANY_INIT,
- .sin6_scope_id = 0,
- };
- auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
- len = sizeof(sin6);
- if (bind(fd, sa, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "bind(IN6ADDR_ANY): %s", strerror(errno));
- return;
- }
-
- // Join the all-routers multicast group, ff02::2%index.
- struct ipv6_mreq all_rtrs = {
- .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
- .ipv6mr_interface = ifIndex,
- };
- len = sizeof(all_rtrs);
- if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
- return;
- }
-}
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
{
@@ -319,6 +226,11 @@
class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
}
+static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
+ jboolean hasConnectivity) {
+ setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
+}
+
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
if (javaFd == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -359,6 +271,7 @@
/*
* JNI registration.
*/
+// clang-format off
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
@@ -370,13 +283,14 @@
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
{ "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
- { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
{ "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
{ "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
+ { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess },
};
+// clang-format on
int register_android_net_NetworkUtils(JNIEnv* env)
{
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e5fddef..77cd5d2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -18,6 +18,14 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_TCP_METRICS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -26,6 +34,7 @@
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -38,6 +47,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -46,24 +57,33 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Map.Entry;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.CaptivePortal;
+import android.net.CaptivePortalData;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
+import android.net.DataStallReportParcelable;
import android.net.ICaptivePortal;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.IConnectivityManager;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
@@ -76,43 +96,47 @@
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.ISocketKeepaliveCallback;
-import android.net.ITetheringEventCallback;
import android.net.InetAddresses;
import android.net.IpMemoryStore;
import android.net.IpPrefix;
import android.net.LinkProperties;
-import android.net.LinkProperties.CompareResult;
import android.net.MatchAllNetworkSpecifier;
import android.net.NattSocketKeepalive;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
+import android.net.NetworkProvider;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
import android.net.NetworkState;
+import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
+import android.net.TetheringManager;
import android.net.UidRange;
import android.net.Uri;
+import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
+import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
@@ -127,6 +151,7 @@
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -150,7 +175,6 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -167,8 +191,8 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
@@ -184,16 +208,14 @@
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
-import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
-import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsFactory;
import com.android.server.utils.PriorityDump;
import com.google.android.collect.Lists;
@@ -213,6 +235,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -222,7 +245,9 @@
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
+import java.util.StringJoiner;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -233,7 +258,6 @@
private static final String DIAG_ARG = "--diag";
public static final String SHORT_ARG = "--short";
- private static final String TETHERING_ARG = "tethering";
private static final String NETWORK_ARG = "networks";
private static final String REQUEST_ARG = "requests";
@@ -265,9 +289,6 @@
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
- // How long to dismiss network notification.
- private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000;
-
// Default to 30s linger time-out. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
@@ -280,8 +301,6 @@
private MockableSystemProperties mSystemProperties;
- private Tethering mTethering;
-
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
@@ -306,7 +325,8 @@
/** Flag indicating if background data is restricted. */
private boolean mRestrictBackground;
- final private Context mContext;
+ private final Context mContext;
+ private final Dependencies mDeps;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -368,10 +388,10 @@
private static final int EVENT_PROXY_HAS_CHANGED = 16;
/**
- * used internally when registering NetworkFactories
- * obj = NetworkFactoryInfo
+ * used internally when registering NetworkProviders
+ * obj = NetworkProviderInfo
*/
- private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
+ private static final int EVENT_REGISTER_NETWORK_PROVIDER = 17;
/**
* used internally when registering NetworkAgents
@@ -407,10 +427,10 @@
private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
/**
- * used internally when registering NetworkFactories
+ * used internally when registering NetworkProviders
* obj = Messenger
*/
- private static final int EVENT_UNREGISTER_NETWORK_FACTORY = 23;
+ private static final int EVENT_UNREGISTER_NETWORK_PROVIDER = 23;
/**
* used internally to expire a wakelock when transitioning
@@ -493,11 +513,11 @@
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
- * obj = String representing URL that Internet probe was redirect to, if it was redirected.
- * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
- * arg2 = NetID.
+ * obj = {@link NetworkTestedResults} representing information sent from NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor. If {@link
+ * NetworkMonitorCallbacks#notifyNetworkTested} is called, this will be null.
*/
- public static final int EVENT_NETWORK_TESTED = 41;
+ private static final int EVENT_NETWORK_TESTED = 41;
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS
@@ -505,7 +525,7 @@
* obj = PrivateDnsConfig
* arg2 = netid
*/
- public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
+ private static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
/**
* Request ConnectivityService display provisioning notification.
@@ -513,12 +533,7 @@
* arg2 = NetID.
* obj = Intent to be launched when notification selected by user, null if !arg1.
*/
- public static final int EVENT_PROVISIONING_NOTIFICATION = 43;
-
- /**
- * This event can handle dismissing notification by given network id.
- */
- public static final int EVENT_TIMEOUT_NOTIFICATION = 44;
+ private static final int EVENT_PROVISIONING_NOTIFICATION = 43;
/**
* Used to specify whether a network should be used even if connectivity is partial.
@@ -527,19 +542,36 @@
* arg2 = whether to remember this choice in the future (1 for true or 0 for false)
* obj = network
*/
- private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
+ private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 44;
+
+ /**
+ * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
+ * Both of the arguments are bitmasks, and the value of bits come from
+ * INetworkMonitor.NETWORK_VALIDATION_PROBE_*.
+ * arg1 = A bitmask to describe which probes are completed.
+ * arg2 = A bitmask to describe which probes are successful.
+ */
+ public static final int EVENT_PROBE_STATUS_CHANGED = 45;
+
+ /**
+ * Event for NetworkMonitor to inform ConnectivityService that captive portal data has changed.
+ * arg1 = unused
+ * arg2 = netId
+ * obj = captive portal data
+ */
+ private static final int EVENT_CAPPORT_DATA_CHANGED = 46;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
- public static final int PROVISIONING_NOTIFICATION_SHOW = 1;
+ private static final int PROVISIONING_NOTIFICATION_SHOW = 1;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be hidden.
*/
- public static final int PROVISIONING_NOTIFICATION_HIDE = 0;
+ private static final int PROVISIONING_NOTIFICATION_HIDE = 0;
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
@@ -550,14 +582,19 @@
.asInterface(ServiceManager.getService("dnsresolver"));
}
- /** Handler thread used for both of the handlers below. */
+ /** Handler thread used for all of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
final private NetworkStateTrackerHandler mTrackerHandler;
+ /** Handler used for processing {@link android.net.ConnectivityDiagnosticsManager} events */
+ @VisibleForTesting
+ final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
+
private final DnsManager mDnsManager;
+ private final NetworkRanker mNetworkRanker;
private boolean mSystemReady;
private Intent mInitialBroadcast;
@@ -581,20 +618,24 @@
// the set of network types that can only be enabled by system/sig apps
private List mProtectedNetworks;
- private TelephonyManager mTelephonyManager;
+ private Set<String> mWolSupportedInterfaces;
+
+ private final TelephonyManager mTelephonyManager;
+ private final AppOpsManager mAppOpsManager;
+
+ private final LocationPermissionChecker mLocationPermissionChecker;
private KeepaliveTracker mKeepaliveTracker;
private NetworkNotificationManager mNotifier;
private LingerMonitor mLingerMonitor;
- // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
- private static final int MIN_NET_ID = 100; // some reserved marks
- private static final int MAX_NET_ID = 65535 - 0x0400; // Top 1024 bits reserved by IpSecService
- private int mNextNetId = MIN_NET_ID;
-
// sequence number of NetworkRequests
private int mNextNetworkRequestId = 1;
+ // Sequence number for NetworkProvider IDs.
+ private final AtomicInteger mNextNetworkProviderId = new AtomicInteger(
+ NetworkProvider.FIRST_PROVIDER_ID);
+
// NetworkRequest activity String log entries.
private static final int MAX_NETWORK_REQUEST_LOGS = 20;
private final LocalLog mNetworkRequestInfoLogs = new LocalLog(MAX_NETWORK_REQUEST_LOGS);
@@ -622,6 +663,10 @@
@VisibleForTesting
final MultipathPolicyTracker mMultipathPolicyTracker;
+ @VisibleForTesting
+ final Map<IBinder, ConnectivityDiagnosticsCallbackInfo> mConnectivityDiagnosticsCallbacks =
+ new HashMap<>();
+
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -698,9 +743,9 @@
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
boolean isDefaultNetwork) {
if (DBG) {
- log("Sending " + state +
- " broadcast for type " + type + " " + nai.name() +
- " isDefaultNetwork=" + isDefaultNetwork);
+ log("Sending " + state
+ + " broadcast for type " + type + " " + nai.toShortString()
+ + " isDefaultNetwork=" + isDefaultNetwork);
}
}
@@ -780,14 +825,6 @@
}
}
- private String naiToString(NetworkAgentInfo nai) {
- String name = nai.name();
- String state = (nai.networkInfo != null) ?
- nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
- "???/???";
- return name + " " + state;
- }
-
public void dump(IndentingPrintWriter pw) {
pw.println("mLegacyTypeTracker:");
pw.increaseIndent();
@@ -802,7 +839,7 @@
for (int type = 0; type < mTypeLists.length; type++) {
if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
for (NetworkAgentInfo nai : mTypeLists[type]) {
- pw.println(type + " " + naiToString(nai));
+ pw.println(type + " " + nai.toShortString());
}
}
}
@@ -835,22 +872,110 @@
}
};
+ /**
+ * Dependencies of ConnectivityService, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get system properties to use in ConnectivityService.
+ */
+ public MockableSystemProperties getSystemProperties() {
+ return new MockableSystemProperties();
+ }
+
+ /**
+ * Create a HandlerThread to use in ConnectivityService.
+ */
+ public HandlerThread makeHandlerThread() {
+ return new HandlerThread("ConnectivityServiceThread");
+ }
+
+ /**
+ * Get a reference to the NetworkStackClient.
+ */
+ public NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
+ /**
+ * @see ProxyTracker
+ */
+ public ProxyTracker makeProxyTracker(@NonNull Context context,
+ @NonNull Handler connServiceHandler) {
+ return new ProxyTracker(context, connServiceHandler, EVENT_PROXY_HAS_CHANGED);
+ }
+
+ /**
+ * @see NetIdManager
+ */
+ public NetIdManager makeNetIdManager() {
+ return new NetIdManager();
+ }
+
+ /**
+ * @see NetworkUtils#queryUserAccess(int, int)
+ */
+ public boolean queryUserAccess(int uid, int netId) {
+ return NetworkUtils.queryUserAccess(uid, netId);
+ }
+
+ /**
+ * @see MultinetworkPolicyTracker
+ */
+ public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
+ @NonNull Context c, @NonNull Handler h, @NonNull Runnable r) {
+ return new MultinetworkPolicyTracker(c, h, r);
+ }
+
+ /**
+ * @see ServiceManager#checkService(String)
+ */
+ public boolean hasService(@NonNull String name) {
+ return ServiceManager.checkService(name) != null;
+ }
+
+ /**
+ * @see IpConnectivityMetrics.Logger
+ */
+ public IpConnectivityMetrics.Logger getMetricsLogger() {
+ return Objects.requireNonNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+ "no IpConnectivityMetrics service");
+ }
+
+ /**
+ * @see IpConnectivityMetrics
+ */
+ public IIpConnectivityMetrics getIpConnectivityMetrics() {
+ return IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+
+ public IBatteryStats getBatteryStatsService() {
+ return BatteryStatsService.getService();
+ }
+ }
+
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
- this(context, netManager, statsService, policyManager,
- getDnsResolver(), new IpConnectivityLog(), NetdService.getInstance());
+ this(context, netManager, statsService, policyManager, getDnsResolver(),
+ new IpConnectivityLog(), NetdService.getInstance(), new Dependencies());
}
@VisibleForTesting
protected ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd) {
+ IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
if (DBG) log("ConnectivityService starting up");
- mSystemProperties = getSystemProperties();
+ mDeps = Objects.requireNonNull(deps, "missing Dependencies");
+ mSystemProperties = mDeps.getSystemProperties();
+ mNetIdManager = mDeps.makeNetIdManager();
+ mContext = Objects.requireNonNull(context, "missing Context");
mMetricsLog = logger;
mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+ mNetworkRanker = new NetworkRanker();
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -864,29 +989,32 @@
mDefaultWifiRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
- mHandlerThread = new HandlerThread("ConnectivityServiceThread");
+ mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
+ mConnectivityDiagnosticsHandler =
+ new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- mContext = checkNotNull(context, "missing Context");
- mNMS = checkNotNull(netManager, "missing INetworkManagementService");
- mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
- mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
- mPolicyManagerInternal = checkNotNull(
+ mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+ mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+ mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
+ mPolicyManagerInternal = Objects.requireNonNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
- mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
- mProxyTracker = makeProxyTracker();
+ mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
+ mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
mNetd = netd;
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mLocationPermissionChecker = new LocationPermissionChecker(mContext);
// To ensure uid rules are synchronized with Network Policy, register for
// NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -950,7 +1078,7 @@
// Do the same for Ethernet, since it's often not specified in the configs, although many
// devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) {
+ if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
mNetworksDefined++;
}
@@ -968,7 +1096,11 @@
}
}
- mTethering = makeTethering();
+ mWolSupportedInterfaces = new ArraySet(
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_wakeonlan_supported_interfaces));
+
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1003,7 +1135,6 @@
mHandler);
try {
- mNMS.registerObserver(mTethering);
mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
@@ -1012,11 +1143,9 @@
mSettingsObserver = new SettingsObserver(mContext, mHandler);
registerSettingsCallbacks();
- final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext);
+ final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext, mHandler);
dataConnectionStats.startMonitoring();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
mContext.getSystemService(NotificationManager.class));
@@ -1029,7 +1158,7 @@
LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS);
mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
- mMultinetworkPolicyTracker = createMultinetworkPolicyTracker(
+ mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
mMultinetworkPolicyTracker.start();
@@ -1039,33 +1168,9 @@
registerPrivateDnsSettingsCallbacks();
}
- @VisibleForTesting
- protected Tethering makeTethering() {
- // TODO: Move other elements into @Overridden getters.
- final TetheringDependencies deps = new TetheringDependencies() {
- @Override
- public boolean isTetheringSupported() {
- return ConnectivityService.this.isTetheringSupported();
- }
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- return mDefaultRequest;
- }
- };
- return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
- IoThread.get().getLooper(), new MockableSystemProperties(),
- deps);
- }
-
- @VisibleForTesting
- protected ProxyTracker makeProxyTracker() {
- return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
- }
-
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
@@ -1075,7 +1180,7 @@
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
if (transportType > -1) {
netCap.addTransportType(transportType);
}
@@ -1151,22 +1256,6 @@
return mNextNetworkRequestId++;
}
- @VisibleForTesting
- protected int reserveNetId() {
- synchronized (mNetworkForNetId) {
- for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
- int netId = mNextNetId;
- if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
- // Make sure NetID unused. http://b/16815182
- if (!mNetIdInUse.get(netId)) {
- mNetIdInUse.put(netId, true);
- return netId;
- }
- }
- }
- throw new IllegalStateException("No free netIds");
- }
-
private NetworkState getFilteredNetworkState(int networkType, int uid) {
if (mLegacyTypeTracker.isTypeSupported(networkType)) {
final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
@@ -1286,10 +1375,9 @@
if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
return;
}
- String action = blocked ? "BLOCKED" : "UNBLOCKED";
- log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
- nri.mUid, nri.request.requestId, net.netId));
- mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ final String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ mNetworkInfoBlockingLogs.log(String.format(
+ "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.netId));
}
/**
@@ -1335,7 +1423,7 @@
@Override
public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
- enforceConnectivityInternalPermission();
+ NetworkStack.checkNetworkStackPermission(mContext);
return getActiveNetworkForUidInternal(uid, ignoreBlocked);
}
@@ -1377,7 +1465,7 @@
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
- enforceConnectivityInternalPermission();
+ NetworkStack.checkNetworkStackPermission(mContext);
final NetworkState state = getUnfilteredActiveNetworkState(uid);
filterNetworkStateForUid(state, uid, ignoreBlocked);
return state.networkInfo;
@@ -1452,7 +1540,8 @@
}
@Override
- public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1474,7 +1563,10 @@
NetworkAgentInfo nai = getDefaultNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
- result.put(nai.network, nc);
+ result.put(
+ nai.network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
synchronized (mVpns) {
@@ -1484,10 +1576,12 @@
Network[] networks = vpn.getUnderlyingNetworks();
if (networks != null) {
for (Network network : networks) {
- nai = getNetworkAgentInfoForNetwork(network);
- nc = getNetworkCapabilitiesInternal(nai);
+ nc = getNetworkCapabilitiesInternal(network);
if (nc != null) {
- result.put(network, nc);
+ result.put(
+ network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
}
}
@@ -1519,57 +1613,65 @@
enforceAccessPermission();
final int uid = Binder.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
- return state.linkProperties;
+ if (state.linkProperties == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
+ Binder.getCallingPid(), uid);
}
@Override
public LinkProperties getLinkPropertiesForType(int networkType) {
enforceAccessPermission();
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) {
- synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
- }
- }
- return null;
+ final LinkProperties lp = getLinkProperties(nai);
+ if (lp == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(
+ lp, Binder.getCallingPid(), Binder.getCallingUid());
}
// TODO - this should be ALL networks
@Override
public LinkProperties getLinkProperties(Network network) {
enforceAccessPermission();
- return getLinkProperties(getNetworkAgentInfoForNetwork(network));
+ final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network));
+ if (lp == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(
+ lp, Binder.getCallingPid(), Binder.getCallingUid());
}
- private LinkProperties getLinkProperties(NetworkAgentInfo nai) {
+ @Nullable
+ private LinkProperties getLinkProperties(@Nullable NetworkAgentInfo nai) {
if (nai == null) {
return null;
}
synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
+ return nai.linkProperties;
}
}
- private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
- if (nai != null) {
- synchronized (nai) {
- if (nai.networkCapabilities != null) {
- return networkCapabilitiesRestrictedForCallerPermissions(
- nai.networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
- }
- }
- }
- return null;
- }
-
- @Override
- public NetworkCapabilities getNetworkCapabilities(Network network) {
- enforceAccessPermission();
+ private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) {
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
- private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
+ private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
+ if (nai == null) return null;
+ synchronized (nai) {
+ if (nai.networkCapabilities == null) return null;
+ return networkCapabilitiesRestrictedForCallerPermissions(
+ nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
+ }
+ }
+
+ @Override
+ public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+ enforceAccessPermission();
+ return maybeSanitizeLocationInfoForCaller(
+ getNetworkCapabilitiesInternal(network),
+ Binder.getCallingUid(), callingPackageName);
+ }
+
+ @VisibleForTesting
+ NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
final NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (!checkSettingsPermission(callerPid, callerUid)) {
@@ -1579,13 +1681,77 @@
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
+ newNc.setAdministratorUids(new int[0]);
+
return newNc;
}
- private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
- if (!checkSettingsPermission()) {
- nc.setSingleUid(Binder.getCallingUid());
+ @VisibleForTesting
+ @Nullable
+ NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+ @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+ if (nc == null) {
+ return null;
}
+ final NetworkCapabilities newNc = new NetworkCapabilities(nc);
+ if (callerUid != newNc.getOwnerUid()) {
+ newNc.setOwnerUid(INVALID_UID);
+ return newNc;
+ }
+
+ // Allow VPNs to see ownership of their own VPN networks - not location sensitive.
+ if (nc.hasTransport(TRANSPORT_VPN)) {
+ // Owner UIDs already checked above. No need to re-check.
+ return newNc;
+ }
+
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+ // Caller does not have the requisite location permissions. Reset the
+ // owner's UID in the NetworkCapabilities.
+ newNc.setOwnerUid(INVALID_UID);
+ }
+ }
+ );
+
+ return newNc;
+ }
+
+ private LinkProperties linkPropertiesRestrictedForCallerPermissions(
+ LinkProperties lp, int callerPid, int callerUid) {
+ if (lp == null) return new LinkProperties();
+
+ // Only do a permission check if sanitization is needed, to avoid unnecessary binder calls.
+ final boolean needsSanitization =
+ (lp.getCaptivePortalApiUrl() != null || lp.getCaptivePortalData() != null);
+ if (!needsSanitization) {
+ return new LinkProperties(lp);
+ }
+
+ if (checkSettingsPermission(callerPid, callerUid)) {
+ return new LinkProperties(lp, true /* parcelSensitiveFields */);
+ }
+
+ final LinkProperties newLp = new LinkProperties(lp);
+ // Sensitive fields would not be parceled anyway, but sanitize for consistency before the
+ // object gets parceled.
+ newLp.setCaptivePortalApiUrl(null);
+ newLp.setCaptivePortalData(null);
+ return newLp;
+ }
+
+ private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
+ int callerUid, String callerPackageName) {
+ if (!checkSettingsPermission()) {
+ nc.setSingleUid(callerUid);
+ }
+ nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
+ nc.setAdministratorUids(new int[0]);
+
+ // Clear owner UID; this can never come from an app.
+ nc.setOwnerUid(INVALID_UID);
}
private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
@@ -1596,8 +1762,8 @@
@Override
public NetworkState[] getAllNetworkState() {
- // Require internal since we're handing out IMSI details
- enforceConnectivityInternalPermission();
+ // This contains IMSI details, so make sure the caller is privileged.
+ NetworkStack.checkNetworkStackPermission(mContext);
final ArrayList<NetworkState> result = Lists.newArrayList();
for (Network network : getAllNetworks()) {
@@ -1625,7 +1791,7 @@
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
- final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+ final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork());
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
@@ -1675,7 +1841,7 @@
}
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
- enforceConnectivityInternalPermission();
+ enforceConnectivityRestrictedNetworksPermission();
}
InetAddress addr;
@@ -1783,11 +1949,9 @@
// the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
// event callback for certain nai. e.g. cellular. Register here to pass to
// NetworkMonitor instead.
- // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
- // registerNetworkAgent to have NetworkMonitor created with system process as design
- // expectation. Also, NetdEventListenerService only allow one callback from each
- // caller type. Need to re-factor NetdEventListenerService to allow multiple
- // NetworkMonitor registrants.
+ // TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
+ // callback from each caller type. Need to re-factor NetdEventListenerService to allow
+ // multiple NetworkMonitor registrants.
if (nai != null && nai.satisfies(mDefaultRequest)) {
nai.networkMonitor().notifyDnsResponse(returnCode);
}
@@ -1800,11 +1964,8 @@
}
};
- @VisibleForTesting
- protected void registerNetdEventCallback() {
- final IIpConnectivityMetrics ipConnectivityMetrics =
- IIpConnectivityMetrics.Stub.asInterface(
- ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ private void registerNetdEventCallback() {
+ final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
if (ipConnectivityMetrics == null) {
Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
return;
@@ -1832,12 +1993,6 @@
}
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
-
- // TODO: relocate this specific callback in Tethering.
- if (restrictBackground) {
- log("onRestrictBackgroundChanged(true): disabling tethering");
- mTethering.untetherAll();
- }
}
};
@@ -1881,7 +2036,7 @@
}
}
- return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+ return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
isNetworkMetered, isBackgroundRestricted);
}
@@ -1948,6 +2103,26 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private void enforceNetworkFactoryPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_FACTORY,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ private void enforceNetworkFactoryOrSettingsPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_FACTORY,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ private void enforceNetworkFactoryOrTestNetworksPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_FACTORY,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkSettingsPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -1961,24 +2136,19 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
}
- private void enforceTetherAccessPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_NETWORK_STATE,
- "ConnectivityService");
- }
-
- private void enforceConnectivityInternalPermission() {
- enforceAnyPermissionOf(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
- }
-
private void enforceControlAlwaysOnVpnPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
"ConnectivityService");
}
+ private void enforceNetworkStackOrSettingsPermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private void enforceNetworkStackSettingsOrSetup() {
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
@@ -1987,16 +2157,32 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private void enforceAirplaneModePermission() {
+ enforceAnyPermissionOf(
+ android.Manifest.permission.NETWORK_AIRPLANE_MODE,
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkNetworkStackPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_STACK,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private boolean checkNetworkStackPermission(int pid, int uid) {
+ return checkAnyPermissionOf(pid, uid,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
return checkAnyPermissionOf(pid, uid,
android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS);
}
private void enforceConnectivityRestrictedNetworksPermission() {
@@ -2006,7 +2192,11 @@
"ConnectivityService");
return;
} catch (SecurityException e) { /* fallback to ConnectivityInternalPermission */ }
- enforceConnectivityInternalPermission();
+ // TODO: Remove this fallback check after all apps have declared
+ // CONNECTIVITY_USE_RESTRICTED_NETWORKS.
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ "ConnectivityService");
}
private void enforceKeepalivePermission() {
@@ -2015,7 +2205,7 @@
// Public because it's used by mLockdownTracker.
public void sendConnectedBroadcast(NetworkInfo info) {
- enforceConnectivityInternalPermission();
+ NetworkStack.checkNetworkStackPermission(mContext);
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -2083,15 +2273,10 @@
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(
ConnectivityManager.EXTRA_NETWORK_INFO);
- if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
- intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- } else {
- BroadcastOptions opts = BroadcastOptions.makeBasic();
- opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
- options = opts.toBundle();
- }
- final IBatteryStats bs = BatteryStatsService.getService();
+ final BroadcastOptions opts = BroadcastOptions.makeBasic();
+ opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+ options = opts.toBundle();
+ final IBatteryStats bs = mDeps.getBatteryStatsService();
try {
bs.noteConnectivityChanged(intent.getIntExtra(
ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
@@ -2108,10 +2293,18 @@
}
}
- void systemReady() {
+ /**
+ * Called when the system is ready and ConnectivityService can initialize remaining components.
+ */
+ @VisibleForTesting
+ public void systemReady() {
+ // Let PermissionMonitor#startMonitoring() running in the beginning of the systemReady
+ // before MultipathPolicyTracker.start(). Since mApps in PermissionMonitor needs to be
+ // populated first to ensure that listening network request which is sent by
+ // MultipathPolicyTracker won't be added NET_CAPABILITY_FOREGROUND capability.
+ mPermissionMonitor.startMonitoring();
mProxyTracker.loadGlobalProxy();
registerNetdEventCallback();
- mTethering.systemReady();
synchronized (this) {
mSystemReady = true;
@@ -2129,8 +2322,6 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
-
- mPermissionMonitor.startMonitoring();
}
/**
@@ -2143,7 +2334,7 @@
final String iface = networkAgent.linkProperties.getInterfaceName();
final int timeout;
- int type = ConnectivityManager.TYPE_NONE;
+ final int type;
if (networkAgent.networkCapabilities.hasTransport(
NetworkCapabilities.TRANSPORT_CELLULAR)) {
@@ -2158,11 +2349,10 @@
15);
type = ConnectivityManager.TYPE_WIFI;
} else {
- // do not track any other networks
- timeout = 0;
+ return; // do not track any other networks
}
- if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
+ if (timeout > 0 && iface != null) {
try {
mNMS.addIdleTimer(iface, timeout, type);
} catch (Exception e) {
@@ -2238,13 +2428,6 @@
@VisibleForTesting
protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
- private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
-
- // Overridden for testing purposes to avoid writing to SystemProperties.
- @VisibleForTesting
- protected MockableSystemProperties getSystemProperties() {
- return new MockableSystemProperties();
- }
private void updateTcpBufferSizes(String tcpBufferSizes) {
String[] values = null;
@@ -2305,10 +2488,12 @@
final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
final long DIAG_TIME_MS = 5000;
for (NetworkAgentInfo nai : networksSortedById()) {
+ PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(nai.network);
// Start gathering diagnostic information.
netDiags.add(new NetworkDiagnostics(
nai.network,
new LinkProperties(nai.linkProperties), // Must be a copy.
+ privateDnsCfg,
DIAG_TIME_MS));
}
@@ -2320,7 +2505,8 @@
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
PriorityDump.dump(mPriorityDumper, fd, writer, args);
}
@@ -2332,9 +2518,6 @@
if (ArrayUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
return;
- } else if (ArrayUtils.contains(args, TETHERING_ARG)) {
- mTethering.dump(fd, pw, args);
- return;
} else if (ArrayUtils.contains(args, NETWORK_ARG)) {
dumpNetworks(pw);
return;
@@ -2343,9 +2526,9 @@
return;
}
- pw.print("NetworkFactories for:");
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- pw.print(" " + nfi.name);
+ pw.print("NetworkProviders for:");
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ pw.print(" " + npi.name);
}
pw.println();
pw.println();
@@ -2396,9 +2579,6 @@
mLegacyTypeTracker.dump(pw);
pw.println();
- mTethering.dump(fd, pw, args);
-
- pw.println();
mKeepaliveTracker.dump(pw);
pw.println();
@@ -2558,15 +2738,25 @@
switch (msg.what) {
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
- final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+ NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ // Make sure the original object is not mutated. NetworkAgent normally
+ // makes a copy of the capabilities when sending the message through
+ // the Messenger, but if this ever changes, not making a defensive copy
+ // here will give attack vectors to clients using this code path.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
+ }
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
+ LinkProperties newLp = (LinkProperties) msg.obj;
+ processLinkPropertiesFromAgent(nai, newLp);
+ handleUpdateLinkProperties(nai, newLp);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2582,8 +2772,8 @@
if (nai.everConnected) {
loge("ERROR: cannot call explicitlySelected on already-connected network");
}
- nai.networkMisc.explicitlySelected = (msg.arg1 == 1);
- nai.networkMisc.acceptUnvalidated = (msg.arg1 == 1) && (msg.arg2 == 1);
+ nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
+ nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
// Mark the network as temporarily accepting partial connectivity so that it
// will be validated (and possibly become default) even if it only provides
// partial internet access. Note that if user connects to partial connectivity
@@ -2591,7 +2781,7 @@
// out of wifi coverage) and if the same wifi is available again, the device
// will auto connect to this wifi even though the wifi has "no internet".
// TODO: Evaluate using a separate setting in IpMemoryStore.
- nai.networkMisc.acceptPartialConnectivity = (msg.arg2 == 1);
+ nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2);
break;
}
case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2605,82 +2795,49 @@
switch (msg.what) {
default:
return false;
+ case EVENT_PROBE_STATUS_CHANGED: {
+ final Integer netId = (Integer) msg.obj;
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ if (nai == null) {
+ break;
+ }
+ final boolean probePrivateDnsCompleted =
+ ((msg.arg1 & NETWORK_VALIDATION_PROBE_PRIVDNS) != 0);
+ final boolean privateDnsBroken =
+ ((msg.arg2 & NETWORK_VALIDATION_PROBE_PRIVDNS) == 0);
+ if (probePrivateDnsCompleted) {
+ if (nai.networkCapabilities.isPrivateDnsBroken() != privateDnsBroken) {
+ nai.networkCapabilities.setPrivateDnsBroken(privateDnsBroken);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ }
+ // Only show the notification when the private DNS is broken and the
+ // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
+ if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) {
+ showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
+ }
+ nai.networkAgentConfig.hasShownBroken = privateDnsBroken;
+ } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
+ // If probePrivateDnsCompleted is false but nai.networkCapabilities says
+ // private DNS is broken, it means this network is being reevaluated.
+ // Either probing private DNS is not necessary any more or it hasn't been
+ // done yet. In either case, the networkCapabilities should be updated to
+ // reflect the new status.
+ nai.networkCapabilities.setPrivateDnsBroken(false);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ nai.networkAgentConfig.hasShownBroken = false;
+ }
+ break;
+ }
case EVENT_NETWORK_TESTED: {
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ final NetworkTestedResults results = (NetworkTestedResults) msg.obj;
+
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(results.mNetId);
if (nai == null) break;
- final boolean wasPartial = nai.partialConnectivity;
- nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
- final boolean partialConnectivityChanged =
- (wasPartial != nai.partialConnectivity);
-
- final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
- // Only show a connected notification if the network is pending validation
- // after the captive portal app was open, and it has now validated.
- if (nai.captivePortalValidationPending && valid) {
- // User is now logged in, network validated.
- nai.captivePortalValidationPending = false;
- showNetworkNotification(nai, NotificationType.LOGGED_IN);
- }
-
- final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
-
- if (DBG) {
- final String logMsg = !TextUtils.isEmpty(redirectUrl)
- ? " with redirect to " + redirectUrl
- : "";
- log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
- }
- if (valid != nai.lastValidated) {
- if (wasDefault) {
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
- }
- final int oldScore = nai.getCurrentScore();
- nai.lastValidated = valid;
- nai.everValidated |= valid;
- updateCapabilities(oldScore, nai, nai.networkCapabilities);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
- if (valid) {
- handleFreshlyValidatedNetwork(nai);
- // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
- // notifications if network becomes valid.
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.NO_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.LOST_INTERNET);
- mNotifier.clearNotification(nai.network.netId,
- NotificationType.PARTIAL_CONNECTIVITY);
- }
- } else if (partialConnectivityChanged) {
- updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
- }
- updateInetCondition(nai);
- // Let the NetworkAgent know the state of its network
- Bundle redirectUrlBundle = new Bundle();
- redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
- // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
- nai.asyncChannel.sendMessage(
- NetworkAgent.CMD_REPORT_NETWORK_STATUS,
- (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
- 0, redirectUrlBundle);
-
- // If NetworkMonitor detects partial connectivity before
- // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
- // immediately. Re-notify partial connectivity silently if no internet
- // notification already there.
- if (!wasPartial && nai.partialConnectivity) {
- // Remove delayed message if there is a pending message.
- mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
- handlePromptUnvalidated(nai.network);
- }
-
- if (wasValidated && !nai.lastValidated) {
- handleNetworkUnvalidated(nai);
- }
+ handleNetworkTested(nai, results.mTestResult,
+ (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);
break;
}
case EVENT_PROVISIONING_NOTIFICATION: {
@@ -2694,7 +2851,7 @@
nai.everCaptivePortalDetected |= visible;
if (nai.lastCaptivePortalDetected &&
Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
- if (DBG) log("Avoiding captive portal network: " + nai.name());
+ if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
teardownUnneededNetwork(nai);
@@ -2712,9 +2869,10 @@
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
break;
}
- if (!nai.networkMisc.provisioningNotificationDisabled) {
+ if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
- (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+ (PendingIntent) msg.obj,
+ nai.networkAgentConfig.explicitlySelected);
}
}
break;
@@ -2726,10 +2884,90 @@
updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
break;
}
+ case EVENT_CAPPORT_DATA_CHANGED: {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ if (nai == null) break;
+ handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
+ break;
+ }
}
return true;
}
+ private void handleNetworkTested(
+ @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
+ final boolean wasPartial = nai.partialConnectivity;
+ nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
+ final boolean partialConnectivityChanged =
+ (wasPartial != nai.partialConnectivity);
+
+ final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
+
+ if (DBG) {
+ final String logMsg = !TextUtils.isEmpty(redirectUrl)
+ ? " with redirect to " + redirectUrl
+ : "";
+ log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ }
+ if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ mDeps.getMetricsLogger()
+ .defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
+ }
+ final int oldScore = nai.getCurrentScore();
+ nai.lastValidated = valid;
+ nai.everValidated |= valid;
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ // If score has changed, rebroadcast to NetworkProviders. b/17726566
+ if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (valid) {
+ handleFreshlyValidatedNetwork(nai);
+ // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+ // LOST_INTERNET notifications if network becomes valid.
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.NO_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.LOST_INTERNET);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PARTIAL_CONNECTIVITY);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PRIVATE_DNS_BROKEN);
+ // If network becomes valid, the hasShownBroken should be reset for
+ // that network so that the notification will be fired when the private
+ // DNS is broken again.
+ nai.networkAgentConfig.hasShownBroken = false;
+ }
+ } else if (partialConnectivityChanged) {
+ updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+ }
+ updateInetCondition(nai);
+ // Let the NetworkAgent know the state of its network
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+ // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, redirectUrlBundle);
+
+ // If NetworkMonitor detects partial connectivity before
+ // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+ // immediately. Re-notify partial connectivity silently if no internet
+ // notification already there.
+ if (!wasPartial && nai.partialConnectivity) {
+ // Remove delayed message if there is a pending message.
+ mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
+ handlePromptUnvalidated(nai.network);
+ }
+
+ if (wasValidated && !nai.lastValidated) {
+ handleNetworkUnvalidated(nai);
+ }
+ }
+
private int getCaptivePortalMode() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_MODE,
@@ -2751,25 +2989,11 @@
return true;
}
- private boolean maybeHandleNetworkFactoryMessage(Message msg) {
- switch (msg.what) {
- default:
- return false;
- case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
- handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
- /* callOnUnavailable */ true);
- break;
- }
- }
- return true;
- }
-
@Override
public void handleMessage(Message msg) {
if (!maybeHandleAsyncChannelMessage(msg)
&& !maybeHandleNetworkMonitorMessage(msg)
- && !maybeHandleNetworkAgentInfoMessage(msg)
- && !maybeHandleNetworkFactoryMessage(msg)) {
+ && !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
@@ -2781,7 +3005,7 @@
private NetworkMonitorCallbacks(NetworkAgentInfo nai) {
mNetId = nai.network.netId;
- mNai = new AutodestructReference(nai);
+ mNai = new AutodestructReference<>(nai);
}
@Override
@@ -2792,8 +3016,36 @@
@Override
public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
- mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
- testResult, mNetId, redirectUrl));
+ // Legacy version of notifyNetworkTestedWithExtras.
+ // Would only be called if the system has a NetworkStack module older than the
+ // framework, which does not happen in practice.
+ Slog.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
+ }
+
+ @Override
+ public void notifyNetworkTestedWithExtras(NetworkTestResultParcelable p) {
+ // Notify mTrackerHandler and mConnectivityDiagnosticsHandler of the event. Both use
+ // the same looper so messages will be processed in sequence.
+ final Message msg = mTrackerHandler.obtainMessage(
+ EVENT_NETWORK_TESTED,
+ new NetworkTestedResults(
+ mNetId, p.result, p.timestampMillis, p.redirectUrl));
+ mTrackerHandler.sendMessage(msg);
+
+ // Invoke ConnectivityReport generation for this Network test event.
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
+ if (nai == null) return;
+ final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
+ new ConnectivityReportEvent(p.timestampMillis, nai));
+
+ final PersistableBundle extras = new PersistableBundle();
+ extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
+ extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
+ extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
+
+ m.setData(new Bundle(extras));
+ mConnectivityDiagnosticsHandler.sendMessage(m);
}
@Override
@@ -2804,6 +3056,20 @@
}
@Override
+ public void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PROBE_STATUS_CHANGED,
+ probesCompleted, probesSucceeded, new Integer(mNetId)));
+ }
+
+ @Override
+ public void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_CAPPORT_DATA_CHANGED,
+ 0, mNetId, data));
+ }
+
+ @Override
public void showProvisioningNotification(String action, String packageName) {
final Intent intent = new Intent(action);
intent.setPackage(packageName);
@@ -2812,7 +3078,11 @@
// Only the system server can register notifications with package "android"
final long token = Binder.clearCallingIdentity();
try {
- pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ pendingIntent = PendingIntent.getBroadcast(
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2828,9 +3098,50 @@
}
@Override
+ public void notifyDataStallSuspected(DataStallReportParcelable p) {
+ ConnectivityService.this.notifyDataStallSuspected(p, mNetId);
+ }
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+ }
+
+ private void notifyDataStallSuspected(DataStallReportParcelable p, int netId) {
+ log("Data stall detected with methods: " + p.detectionMethod);
+
+ final PersistableBundle extras = new PersistableBundle();
+ int detectionMethod = 0;
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+ extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, p.dnsConsecutiveTimeouts);
+ detectionMethod |= DETECTION_METHOD_DNS_EVENTS;
+ }
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+ extras.putInt(KEY_TCP_PACKET_FAIL_RATE, p.tcpPacketFailRate);
+ extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS,
+ p.tcpMetricsCollectionPeriodMillis);
+ detectionMethod |= DETECTION_METHOD_TCP_METRICS;
+ }
+
+ final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, detectionMethod, netId,
+ p.timestampMillis);
+ msg.setData(new Bundle(extras));
+
+ // NetworkStateTrackerHandler currently doesn't take any actions based on data
+ // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+ // the cost of going through two handlers.
+ mConnectivityDiagnosticsHandler.sendMessage(msg);
+ }
+
+ private boolean hasDataStallDetectionMethod(DataStallReportParcelable p, int detectionMethod) {
+ return (p.detectionMethod & detectionMethod) != 0;
}
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
@@ -2907,56 +3218,58 @@
}
}
- nai.clatd.setNat64Prefix(prefix);
+ nai.clatd.setNat64PrefixFromDns(prefix);
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
- private void updateLingerState(NetworkAgentInfo nai, long now) {
+ private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
+ @Nullable final CaptivePortalData data) {
+ nai.captivePortalData = data;
+ // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+ }
+
+ /**
+ * Updates the linger state from the network requests inside the NAI.
+ * @param nai the agent info to update
+ * @param now the timestamp of the event causing this update
+ * @return whether the network was lingered as a result of this update
+ */
+ private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
// one lingered request, start lingering.
nai.updateLingerTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.name());
+ if (DBG) log("Unlingering " + nai.toShortString());
nai.unlinger();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
} else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
- int lingerTime = (int) (nai.getLingerExpiry() - now);
- if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+ if (DBG) {
+ final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+ }
nai.linger();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+ return true;
}
+ return false;
}
private void handleAsyncChannelHalfConnect(Message msg) {
- AsyncChannel ac = (AsyncChannel) msg.obj;
- if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
+ ensureRunningOnConnectivityServiceThread();
+ final AsyncChannel ac = (AsyncChannel) msg.obj;
+ if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
- mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage(
- AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- // A network factory has connected. Send it all current NetworkRequests.
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkFactory.SerialNumber.NONE;
- }
- ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, serial,
- nri.request);
- }
+ NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
+ npi.completeConnection();
+ sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
- mNetworkFactoryInfos.remove(msg.obj);
+ mNetworkProviderInfos.remove(msg.obj);
}
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -2971,8 +3284,8 @@
final boolean wasDefault = isDefaultNetwork(nai);
synchronized (mNetworkForNetId) {
mNetworkForNetId.remove(nai.network.netId);
- mNetIdInUse.delete(nai.network.netId);
}
+ mNetIdManager.releaseNetId(nai.network.netId);
// Just in case.
mLegacyTypeTracker.remove(nai, wasDefault);
}
@@ -2988,8 +3301,8 @@
if (nai != null) {
disconnectAndDestroyNetwork(nai);
} else {
- NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
- if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
+ NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
+ if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
}
}
@@ -2997,8 +3310,9 @@
// ConnectivityService, free its interfaces and clean up.
// Must be called on the Handler thread.
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
+ ensureRunningOnConnectivityServiceThread();
if (DBG) {
- log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
+ log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
// Clear all notifications of this network.
mNotifier.clearNotification(nai.network.netId);
@@ -3019,7 +3333,7 @@
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+ mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -3043,23 +3357,25 @@
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
- NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ final NetworkAgentInfo currentNetwork = nri.mSatisfier;
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
- clearNetworkForRequest(request.requestId);
+ nri.mSatisfier = null;
sendUpdatedScoreToFactories(request, null);
}
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
+ mDefaultNetworkNai = null;
updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
- ensureNetworkTransitionWakelock(nai.name());
+ ensureNetworkTransitionWakelock(nai.toShortString());
}
mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
updateAllVpnsCapabilities();
}
- rematchAllNetworksAndRequests(null, 0);
+ rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
// Tell netd to clean up the configuration for this network
@@ -3068,14 +3384,12 @@
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
// after we've rematched networks with requests which should make a potential
// fallback network the default or requested a new network from the
- // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
+ // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
// long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
}
- synchronized (mNetworkForNetId) {
- mNetIdInUse.delete(nai.network.netId);
- }
+ mNetIdManager.releaseNetId(nai.network.netId);
}
private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
@@ -3083,13 +3397,15 @@
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
mNetd.networkCreateVpn(networkAgent.network.netId,
- (networkAgent.networkMisc == null
- || !networkAgent.networkMisc.allowBypass));
+ (networkAgent.networkAgentConfig == null
+ || !networkAgent.networkAgentConfig.allowBypass));
} else {
mNetd.networkCreatePhysical(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
}
mDnsResolver.createNetworkCache(networkAgent.network.netId);
+ mDnsManager.updateTransportsForNetwork(networkAgent.network.netId,
+ networkAgent.networkCapabilities.getTransportTypes());
return true;
} catch (RemoteException | ServiceSpecificException e) {
loge("Error creating network " + networkAgent.network.netId + ": "
@@ -3136,6 +3452,7 @@
}
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
if (nri.request.isListen()) {
@@ -3146,8 +3463,8 @@
}
}
}
- rematchAllNetworksAndRequests(null, 0);
- if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) {
+ rematchAllNetworksAndRequests();
+ if (nri.request.isRequest() && nri.mSatisfier == null) {
sendUpdatedScoreToFactories(nri.request, null);
}
}
@@ -3169,6 +3486,7 @@
// - UnneededFor.LINGER: foreground NetworkRequests. If a network is unneeded for this reason,
// then it should be lingered.
private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
+ ensureRunningOnConnectivityServiceThread();
final int numRequests;
switch (reason) {
case TEARDOWN:
@@ -3202,8 +3520,8 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- getNetworkForRequest(nri.request.requestId).getCurrentScore() <
- nai.getCurrentScoreAsValidated())) {
+ nri.mSatisfier.getCurrentScore()
+ < nai.getCurrentScoreAsValidated())) {
return false;
}
}
@@ -3215,7 +3533,8 @@
final NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
- if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
+ if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
+ && nri.mUid != callingUid) {
log(String.format("UID %d attempted to %s for unowned request %s",
callingUid, requestedOperation, nri));
return null;
@@ -3226,10 +3545,11 @@
}
private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
if (mNetworkRequests.get(nri.request) == null) {
return;
}
- if (getNetworkForRequest(nri.request.requestId) != null) {
+ if (nri.mSatisfier != null) {
return;
}
if (VDBG || (DBG && nri.request.isRequest())) {
@@ -3256,43 +3576,37 @@
}
private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
+
nri.unlinkDeathRecipient();
mNetworkRequests.remove(nri.request);
- synchronized (mUidToNetworkRequestCount) {
- int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
- if (requests < 1) {
- Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
- nri.mUid);
- } else if (requests == 1) {
- mUidToNetworkRequestCount.removeAt(
- mUidToNetworkRequestCount.indexOfKey(nri.mUid));
- } else {
- mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
- }
- }
+ decrementNetworkRequestPerUidCount(nri);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
- NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId);
+ final NetworkAgentInfo nai = nri.mSatisfier;
if (nai != null) {
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
nai.removeRequest(nri.request.requestId);
if (VDBG || DDBG) {
- log(" Removing from current network " + nai.name() +
- ", leaving " + nai.numNetworkRequests() + " requests.");
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
}
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
- updateLingerState(nai, SystemClock.elapsedRealtime());
+ final long now = SystemClock.elapsedRealtime();
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
teardownUnneededNetwork(nai);
} else {
wasKept = true;
}
- clearNetworkForRequest(nri.request.requestId);
+ nri.mSatisfier = null;
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -3324,9 +3638,8 @@
}
}
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST,
- nri.request);
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(nri.request);
}
} else {
// listens don't have a singular affectedNetwork. Check all networks to see
@@ -3341,6 +3654,19 @@
}
}
+ private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
+ synchronized (mUidToNetworkRequestCount) {
+ final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+ if (requests < 1) {
+ Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
+ } else if (requests == 1) {
+ mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+ } else {
+ mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+ }
+ }
+ }
+
@Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceNetworkStackSettingsOrSetup();
@@ -3376,18 +3702,17 @@
return;
}
- if (!nai.networkMisc.explicitlySelected) {
+ if (!nai.networkAgentConfig.explicitlySelected) {
Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
}
- if (accept != nai.networkMisc.acceptUnvalidated) {
- int oldScore = nai.getCurrentScore();
- nai.networkMisc.acceptUnvalidated = accept;
+ if (accept != nai.networkAgentConfig.acceptUnvalidated) {
+ nai.networkAgentConfig.acceptUnvalidated = accept;
// If network becomes partial connectivity and user already accepted to use this
// network, we should respect the user's option and don't need to popup the
// PARTIAL_CONNECTIVITY notification to user again.
- nai.networkMisc.acceptPartialConnectivity = accept;
- rematchAllNetworksAndRequests(nai, oldScore);
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
+ rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
}
@@ -3423,8 +3748,8 @@
return;
}
- if (accept != nai.networkMisc.acceptPartialConnectivity) {
- nai.networkMisc.acceptPartialConnectivity = accept;
+ if (accept != nai.networkAgentConfig.acceptPartialConnectivity) {
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
}
// TODO: Use the current design or save the user choice into IpMemoryStore.
@@ -3456,9 +3781,8 @@
return;
}
if (!nai.avoidUnvalidated) {
- int oldScore = nai.getCurrentScore();
nai.avoidUnvalidated = true;
- rematchAllNetworksAndRequests(nai, oldScore);
+ rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
}
}
@@ -3472,7 +3796,7 @@
@Override
public void startCaptivePortalApp(Network network) {
- enforceConnectivityInternalPermission();
+ enforceNetworkStackOrSettingsPermission();
mHandler.post(() -> {
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return;
@@ -3500,12 +3824,6 @@
new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe,
- // and captivePortalValidationPending is volatile.
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null) {
- nai.captivePortalValidationPending = true;
- }
Binder.withCleanCallingIdentity(() ->
mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
}
@@ -3523,17 +3841,33 @@
enforceSettingsPermission();
}
- // getNetworkAgentInfoForNetwork is thread-safe
- final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
- if (nai == null) return;
-
- // nai.networkMonitor() is thread-safe
- final NetworkMonitorManager nm = nai.networkMonitor();
+ final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
if (nm == null) return;
nm.notifyCaptivePortalAppFinished(response);
}
@Override
+ public void appRequest(final int request) {
+ final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
+ if (nm == null) return;
+
+ if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
+ checkNetworkStackPermission();
+ nm.forceReevaluation(Binder.getCallingUid());
+ }
+ }
+
+ @Nullable
+ private NetworkMonitorManager getNetworkMonitorManager(final Network network) {
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) return null;
+
+ // nai.networkMonitor() is thread-safe
+ return nai.networkMonitor();
+ }
+
+ @Override
public void logEvent(int eventId, String packageName) {
enforceSettingsPermission();
@@ -3559,7 +3893,7 @@
private void rematchForAvoidBadWifiUpdate() {
- rematchAllNetworksAndRequests(null, 0);
+ rematchAllNetworksAndRequests();
for (NetworkAgentInfo nai: mNetworkAgentInfos.values()) {
if (nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
sendUpdatedScoreToFactories(nai);
@@ -3597,30 +3931,36 @@
pw.increaseIndent();
for (NetworkAgentInfo nai : networksSortedById()) {
if (nai.avoidUnvalidated) {
- pw.println(nai.name());
+ pw.println(nai.toShortString());
}
}
pw.decreaseIndent();
pw.decreaseIndent();
}
+ // TODO: This method is copied from TetheringNotificationUpdater. Should have a utility class to
+ // unify the method.
+ private static @NonNull String getSettingsPackageName(@NonNull final PackageManager pm) {
+ final Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
+ final ComponentName settingsComponent = settingsIntent.resolveActivity(pm);
+ return settingsComponent != null
+ ? settingsComponent.getPackageName() : "com.android.settings";
+ }
+
private void showNetworkNotification(NetworkAgentInfo nai, NotificationType type) {
final String action;
final boolean highPriority;
switch (type) {
- case LOGGED_IN:
- action = Settings.ACTION_WIFI_SETTINGS;
- mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION,
- nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS);
- // High priority because it is a direct result of the user logging in to a portal.
- highPriority = true;
- break;
case NO_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
// High priority because it is only displayed for explicitly selected networks.
highPriority = true;
break;
+ case PRIVATE_DNS_BROKEN:
+ action = Settings.ACTION_WIRELESS_SETTINGS;
+ // High priority because we should let user know why there is no internet.
+ highPriority = true;
+ break;
case LOST_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
// High priority because it could help the user avoid unexpected data usage.
@@ -3630,7 +3970,7 @@
action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
// Don't bother the user with a high-priority notification if the network was not
// explicitly selected by the user.
- highPriority = nai.networkMisc.explicitlySelected;
+ highPriority = nai.networkAgentConfig.explicitlySelected;
break;
default:
Slog.wtf(TAG, "Unknown notification type " + type);
@@ -3638,15 +3978,23 @@
}
Intent intent = new Intent(action);
- if (type != NotificationType.LOGGED_IN) {
+ if (type != NotificationType.PRIVATE_DNS_BROKEN) {
intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClassName("com.android.settings",
- "com.android.settings.wifi.WifiNoInternetDialog");
+ // Some OEMs have their own Settings package. Thus, need to get the current using
+ // Settings package name instead of just use default name "com.android.settings".
+ final String settingsPkgName = getSettingsPackageName(mContext.getPackageManager());
+ intent.setClassName(settingsPkgName,
+ settingsPkgName + ".wifi.WifiNoInternetDialog");
}
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null /* options */,
+ UserHandle.CURRENT);
mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, highPriority);
}
@@ -3663,14 +4011,15 @@
// automatically connects to a network that has partial Internet access, the user will
// always be able to use it, either because they've already chosen "don't ask again" or
// because we have prompt them.
- if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+ if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) {
return true;
}
// If a network has no Internet access, only prompt if the network was explicitly selected
// and if the user has not already told us to use the network regardless of whether it
// validated or not.
- if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+ if (nai.networkAgentConfig.explicitlySelected
+ && !nai.networkAgentConfig.acceptUnvalidated) {
return true;
}
@@ -3703,7 +4052,7 @@
private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
NetworkCapabilities nc = nai.networkCapabilities;
- if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
+ if (DBG) log("handleNetworkUnvalidated " + nai.toShortString() + " cap=" + nc);
if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return;
@@ -3758,12 +4107,12 @@
handleApplyDefaultProxy((ProxyInfo)msg.obj);
break;
}
- case EVENT_REGISTER_NETWORK_FACTORY: {
- handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
+ case EVENT_REGISTER_NETWORK_PROVIDER: {
+ handleRegisterNetworkProvider((NetworkProviderInfo) msg.obj);
break;
}
- case EVENT_UNREGISTER_NETWORK_FACTORY: {
- handleUnregisterNetworkFactory((Messenger)msg.obj);
+ case EVENT_UNREGISTER_NETWORK_PROVIDER: {
+ handleUnregisterNetworkProvider((Messenger) msg.obj);
break;
}
case EVENT_REGISTER_NETWORK_AGENT: {
@@ -3853,188 +4202,59 @@
case EVENT_DATA_SAVER_CHANGED:
handleRestrictBackgroundChanged(toBool(msg.arg1));
break;
- case EVENT_TIMEOUT_NOTIFICATION:
- mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN);
- break;
}
}
}
- // javadoc from interface
@Override
- public int tether(String iface, String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- if (isTetheringSupported()) {
- return mTethering.tether(iface);
- } else {
- return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
- }
- }
-
- // javadoc from interface
- @Override
- public int untether(String iface, String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-
- if (isTetheringSupported()) {
- return mTethering.untether(iface);
- } else {
- return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
- }
- }
-
- // javadoc from interface
- @Override
+ @Deprecated
public int getLastTetherError(String iface) {
- enforceTetherAccessPermission();
-
- if (isTetheringSupported()) {
- return mTethering.getLastTetherError(iface);
- } else {
- return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
- }
- }
-
- // TODO - proper iface API for selection by property, inspection, etc
- @Override
- public String[] getTetherableUsbRegexs() {
- enforceTetherAccessPermission();
- if (isTetheringSupported()) {
- return mTethering.getTetherableUsbRegexs();
- } else {
- return new String[0];
- }
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+ return tm.getLastTetherError(iface);
}
@Override
- public String[] getTetherableWifiRegexs() {
- enforceTetherAccessPermission();
- if (isTetheringSupported()) {
- return mTethering.getTetherableWifiRegexs();
- } else {
- return new String[0];
- }
- }
-
- @Override
- public String[] getTetherableBluetoothRegexs() {
- enforceTetherAccessPermission();
- if (isTetheringSupported()) {
- return mTethering.getTetherableBluetoothRegexs();
- } else {
- return new String[0];
- }
- }
-
- @Override
- public int setUsbTethering(boolean enable, String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- if (isTetheringSupported()) {
- return mTethering.setUsbTethering(enable);
- } else {
- return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
- }
- }
-
- // TODO - move iface listing, queries, etc to new module
- // javadoc from interface
- @Override
+ @Deprecated
public String[] getTetherableIfaces() {
- enforceTetherAccessPermission();
- return mTethering.getTetherableIfaces();
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+ return tm.getTetherableIfaces();
}
@Override
+ @Deprecated
public String[] getTetheredIfaces() {
- enforceTetherAccessPermission();
- return mTethering.getTetheredIfaces();
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+ return tm.getTetheredIfaces();
}
+
@Override
+ @Deprecated
public String[] getTetheringErroredIfaces() {
- enforceTetherAccessPermission();
- return mTethering.getErroredIfaces();
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+
+ return tm.getTetheringErroredIfaces();
}
@Override
- public String[] getTetheredDhcpRanges() {
- enforceConnectivityInternalPermission();
- return mTethering.getTetheredDhcpRanges();
+ @Deprecated
+ public String[] getTetherableUsbRegexs() {
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+
+ return tm.getTetherableUsbRegexs();
}
@Override
- public boolean isTetheringSupported(String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- return isTetheringSupported();
- }
-
- // if ro.tether.denied = true we default to no tethering
- // gservices could set the secure setting to 1 though to enable it on a build where it
- // had previously been turned off.
- private boolean isTetheringSupported() {
- int defaultVal = encodeBool(!mSystemProperties.get("ro.tether.denied").equals("true"));
- boolean tetherSupported = toBool(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TETHER_SUPPORTED, defaultVal));
- boolean tetherEnabledInSettings = tetherSupported
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
- // Elevate to system UID to avoid caller requiring MANAGE_USERS permission.
- boolean adminUser = false;
- final long token = Binder.clearCallingIdentity();
- try {
- adminUser = mUserManager.isAdminUser();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return tetherEnabledInSettings && adminUser && mTethering.hasTetherableConfiguration();
- }
-
- @Override
- public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
- String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- if (!isTetheringSupported()) {
- receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
- return;
- }
- mTethering.startTethering(type, receiver, showProvisioningUi);
- }
-
- @Override
- public void stopTethering(int type, String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.stopTethering(type);
- }
-
- /**
- * Get the latest value of the tethering entitlement check.
- *
- * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
- * out some such apps are observed to abuse this API, change to per-UID limits on this API
- * if it's really needed.
- */
- @Override
- public void getLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
- boolean showEntitlementUi, String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.getLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
- }
-
- /** Register tethering event callback. */
- @Override
- public void registerTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.registerTetheringEventCallback(callback);
- }
-
- /** Unregister tethering event callback. */
- @Override
- public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
- String callerPkg) {
- ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.unregisterTetheringEventCallback(callback);
+ @Deprecated
+ public String[] getTetherableWifiRegexs() {
+ final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+ Context.TETHERING_SERVICE);
+ return tm.getTetherableWifiRegexs();
}
// Called when we lose the default network and have no replacement yet.
@@ -4103,6 +4323,24 @@
enforceInternetPermission();
final int uid = Binder.getCallingUid();
final int connectivityInfo = encodeBool(hasConnectivity);
+
+ // Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
+ // forces an ordering of ConnectivityDiagnostics events in the case where hasConnectivity
+ // does not match the known connectivity of the network - this causes NetworkMonitor to
+ // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
+ final NetworkAgentInfo nai;
+ if (network == null) {
+ nai = getDefaultNetwork();
+ } else {
+ nai = getNetworkAgentInfoForNetwork(network);
+ }
+ if (nai != null) {
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+ connectivityInfo, 0, nai));
+ }
+
mHandler.sendMessage(
mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
}
@@ -4159,7 +4397,7 @@
return null;
}
return getLinkPropertiesProxyInfo(activeNetwork);
- } else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
+ } else if (mDeps.queryUserAccess(Binder.getCallingUid(), network.netId)) {
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
// caller may not have.
return getLinkPropertiesProxyInfo(network);
@@ -4168,10 +4406,6 @@
return null;
}
- @VisibleForTesting
- protected boolean queryUserAccess(int uid, int netId) {
- return NetworkUtils.queryUserAccess(uid, netId);
- }
private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
@@ -4184,7 +4418,7 @@
@Override
public void setGlobalProxy(final ProxyInfo proxyProperties) {
- enforceConnectivityInternalPermission();
+ NetworkStack.checkNetworkStackPermission(mContext);
mProxyTracker.setGlobalProxy(proxyProperties);
}
@@ -4242,7 +4476,7 @@
public void onChange(boolean selfChange, Uri uri) {
final Integer what = mUriEventMap.get(uri);
if (what != null) {
- mHandler.obtainMessage(what.intValue()).sendToTarget();
+ mHandler.obtainMessage(what).sendToTarget();
} else {
loge("No matching event to send for URI=" + uri);
}
@@ -4284,7 +4518,7 @@
throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- return vpn.prepare(oldPackage, newPackage);
+ return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
} else {
return false;
}
@@ -4292,26 +4526,29 @@
}
/**
- * Set whether the VPN package has the ability to launch VPNs without user intervention.
- * This method is used by system-privileged apps.
- * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
- * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param packageName The package for which authorization state should change.
* @param userId User for whom {@code packageName} is installed.
* @param authorized {@code true} if this app should be able to start a VPN connection without
- * explicit user approval, {@code false} if not.
- *
+ * explicit user approval, {@code false} if not.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
* @hide
*/
@Override
- public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- vpn.setPackageAuthorization(packageName, authorized);
+ vpn.setPackageAuthorization(packageName, vpnType);
}
}
}
@@ -4333,6 +4570,78 @@
}
/**
+ * Stores the given VPN profile based on the provisioning package name.
+ *
+ * <p>If there is already a VPN profile stored for the provisioning package, this call will
+ * overwrite the profile.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @return {@code true} if user consent has already been granted, {@code false} otherwise.
+ * @hide
+ */
+ @Override
+ public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
+ }
+ }
+
+ /**
+ * Deletes the stored VPN profile for the provisioning package
+ *
+ * <p>If there are no profiles for the given package, this method will silently succeed.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void deleteVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Starts the VPN based on the stored profile for the given package
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @throws IllegalArgumentException if no profile was found for the given package name.
+ * @hide
+ */
+ @Override
+ public void startVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ mVpns.get(user).startVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Stops the Platform VPN if the provided package is running one.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void stopVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).stopVpnProfile(packageName);
+ }
+ }
+
+ /**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
*/
@@ -4505,7 +4814,7 @@
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
+ setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile));
} else {
setLockdownTracker(null);
}
@@ -4535,6 +4844,13 @@
}
}
+ /**
+ * Throws if there is any currently running, always-on Legacy VPN.
+ *
+ * <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
+ * running across the entire system. Tracking for app-based VPNs is done on a per-user,
+ * per-package basis in Vpn.java
+ */
@GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
@@ -4559,7 +4875,7 @@
return false;
}
- return vpn.startAlwaysOnVpn();
+ return vpn.startAlwaysOnVpn(mKeyStore);
}
}
@@ -4574,7 +4890,7 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- return vpn.isAlwaysOnPackageSupported(packageName);
+ return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
}
}
@@ -4595,11 +4911,11 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
return false;
}
}
@@ -4679,12 +4995,10 @@
private static final String ATTR_MNC = "mnc";
private String getProvisioningUrlBaseFromFile() {
- FileReader fileReader = null;
- XmlPullParser parser = null;
+ XmlPullParser parser;
Configuration config = mContext.getResources().getConfiguration();
- try {
- fileReader = new FileReader(mProvisioningUrlFile);
+ try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) {
parser = Xml.newPullParser();
parser.setInput(fileReader);
XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
@@ -4719,19 +5033,13 @@
loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
} catch (IOException e) {
loge("I/O exception reading Carrier Provisioning Urls file: " + e);
- } finally {
- if (fileReader != null) {
- try {
- fileReader.close();
- } catch (IOException e) {}
- }
}
return null;
}
@Override
public String getMobileProvisioningUrl() {
- enforceConnectivityInternalPermission();
+ enforceSettingsPermission();
String url = getProvisioningUrlBaseFromFile();
if (TextUtils.isEmpty(url)) {
url = mContext.getResources().getString(R.string.mobile_provisioning_url);
@@ -4757,14 +5065,14 @@
@Override
public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) {
- enforceConnectivityInternalPermission();
+ enforceSettingsPermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return;
}
final long ident = Binder.clearCallingIdentity();
try {
// Concatenate the range of types onto the range of NetIDs.
- int id = MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
+ int id = NetIdManager.MAX_NET_ID + 1 + (networkType - ConnectivityManager.TYPE_NONE);
mNotifier.setProvNotificationVisible(visible, id, action);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -4773,7 +5081,7 @@
@Override
public void setAirplaneMode(boolean enable) {
- enforceNetworkStackSettingsOrSetup();
+ enforceAirplaneModePermission();
final long ident = Binder.clearCallingIdentity();
try {
final ContentResolver cr = mContext.getContentResolver();
@@ -4793,7 +5101,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -4864,7 +5172,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ userId);
- vpn.startAlwaysOnVpn();
+ vpn.startAlwaysOnVpn(mKeyStore);
}
}
}
@@ -4886,7 +5194,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
}
}
@@ -4946,7 +5254,7 @@
}
};
- private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<>();
+ private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@@ -4954,18 +5262,73 @@
@GuardedBy("mUidToNetworkRequestCount")
private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
- private static class NetworkFactoryInfo {
+ private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
- public final AsyncChannel asyncChannel;
- public final int factorySerialNumber;
+ private final AsyncChannel mAsyncChannel;
+ private final IBinder.DeathRecipient mDeathRecipient;
+ public final int providerId;
- NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int factorySerialNumber) {
+ NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
+ int providerId, IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
- this.asyncChannel = asyncChannel;
- this.factorySerialNumber = factorySerialNumber;
+ this.providerId = providerId;
+ mAsyncChannel = asyncChannel;
+ mDeathRecipient = deathRecipient;
+
+ if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
+ throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+ }
+ }
+
+ boolean isLegacyNetworkFactory() {
+ return mAsyncChannel != null;
+ }
+
+ void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
+ try {
+ messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
+ } catch (RemoteException e) {
+ // Remote process died. Ignore; the death recipient will remove this
+ // NetworkProviderInfo from mNetworkProviderInfos.
+ }
+ }
+
+ void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
+ if (isLegacyNetworkFactory()) {
+ mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
+ servingProviderId, request);
+ } else {
+ sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+ servingProviderId, request);
+ }
+ }
+
+ void cancelRequest(NetworkRequest request) {
+ if (isLegacyNetworkFactory()) {
+ mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
+ } else {
+ sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
+ }
+ }
+
+ void connect(Context context, Handler handler) {
+ if (isLegacyNetworkFactory()) {
+ mAsyncChannel.connect(context, handler, messenger);
+ } else {
+ try {
+ messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
+ }
+ }
+ }
+
+ void completeConnection() {
+ if (isLegacyNetworkFactory()) {
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ }
}
}
@@ -4982,6 +5345,11 @@
*/
private class NetworkRequestInfo implements IBinder.DeathRecipient {
final NetworkRequest request;
+ // The network currently satisfying this request, or null if none. Must only be touched
+ // on the handler thread. This only makes sense for network requests and not for listens,
+ // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+ @Nullable
+ NetworkAgentInfo mSatisfier;
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5018,6 +5386,10 @@
}
}
+ NetworkRequestInfo(NetworkRequest r) {
+ this(r, null);
+ }
+
private void enforceRequestCountLimit() {
synchronized (mUidToNetworkRequestCount) {
int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
@@ -5042,8 +5414,8 @@
}
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + request +
- (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+ return "uid/pid:" + mUid + "/" + mPid + " " + request
+ + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -5054,11 +5426,11 @@
}
}
- // This checks that the passed capabilities either do not request a specific SSID/SignalStrength
- // , or the calling app has permission to do so.
+ // This checks that the passed capabilities either do not request a
+ // specific SSID/SignalStrength, or the calling app has permission to do so.
private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
- int callerPid, int callerUid) {
- if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
+ int callerPid, int callerUid, String callerPackageName) {
+ if (null != nc.getSsid() && !checkSettingsPermission(callerPid, callerUid)) {
throw new SecurityException("Insufficient permissions to request a specific SSID");
}
@@ -5067,6 +5439,7 @@
throw new SecurityException(
"Insufficient permissions to request a specific signal strength");
}
+ mAppOpsManager.checkPackage(callerUid, callerPackageName);
}
private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
@@ -5096,7 +5469,7 @@
detail = reason;
}
log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
- detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+ detail, Arrays.toString(thresholdsArray.toArray()), nai.toShortString()));
}
nai.asyncChannel.sendMessage(
@@ -5113,12 +5486,36 @@
return;
}
MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
- ns.assertValidFromUid(Binder.getCallingUid());
+ }
+
+ private void ensureValid(NetworkCapabilities nc) {
+ ensureValidNetworkSpecifier(nc);
+ if (nc.isPrivateDnsBroken()) {
+ throw new IllegalArgumentException("Can't request broken private DNS");
+ }
+ }
+
+ private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ final int userId = UserHandle.getCallingUserId();
+ try {
+ final int callingVersion = pm.getApplicationInfoAsUser(
+ callingPackageName, 0 /* flags */, userId).targetSdkVersion;
+ if (callingVersion < version) return false;
+ } catch (PackageManager.NameNotFoundException e) { }
+ return true;
}
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+ Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
+ @NonNull String callingPackageName) {
+ if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
+ if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+ throw new SecurityException("Insufficient permissions to specify legacy type");
+ }
+ }
+ final int callingUid = Binder.getCallingUid();
final NetworkRequest.Type type = (networkCapabilities == null)
? NetworkRequest.Type.TRACK_DEFAULT
: NetworkRequest.Type.REQUEST;
@@ -5126,7 +5523,7 @@
// the default network request. This allows callers to keep track of
// the system default network.
if (type == NetworkRequest.Type.TRACK_DEFAULT) {
- networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
+ networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
enforceAccessPermission();
} else {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -5138,18 +5535,19 @@
}
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), callingUid, callingPackageName);
// Set the UID range for this request to the single UID of the requester, or to an empty
// set of UIDs if the caller has the appropriate permission and UIDs have not been set.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
// are no visible methods to set the UIDs, an app could use reflection to try and get
// networks for other apps so it's essential that the UIDs are overwritten.
- restrictRequestUidsForCaller(networkCapabilities);
+ restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+ callingUid, callingPackageName);
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), type);
@@ -5188,7 +5586,7 @@
final int uid = Binder.getCallingUid();
Integer uidReqs = mBandwidthRequests.get(uid);
if (uidReqs == null) {
- uidReqs = new Integer(0);
+ uidReqs = 0;
}
mBandwidthRequests.put(uid, ++uidReqs);
}
@@ -5219,16 +5617,18 @@
@Override
public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation) {
- checkNotNull(operation, "PendingIntent cannot be null.");
+ PendingIntent operation, @NonNull String callingPackageName) {
+ Objects.requireNonNull(operation, "PendingIntent cannot be null.");
+ final int callingUid = Binder.getCallingUid();
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), callingUid, callingPackageName);
ensureValidNetworkSpecifier(networkCapabilities);
- restrictRequestUidsForCaller(networkCapabilities);
+ restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+ callingUid, callingPackageName);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -5247,7 +5647,7 @@
@Override
public void releasePendingNetworkRequest(PendingIntent operation) {
- checkNotNull(operation, "PendingIntent cannot be null.");
+ Objects.requireNonNull(operation, "PendingIntent cannot be null.");
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
getCallingUid(), 0, operation));
}
@@ -5276,22 +5676,23 @@
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, IBinder binder) {
+ Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+ final int callingUid = Binder.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
- restrictRequestUidsForCaller(nc);
+ Binder.getCallingPid(), callingUid, callingPackageName);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
// make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
// onLost and onAvailable callbacks when networks move in and out of the background.
// There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
// can't request networks.
restrictBackgroundRequestForCaller(nc);
- ensureValidNetworkSpecifier(nc);
+ ensureValid(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -5304,17 +5705,17 @@
@Override
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation) {
- checkNotNull(operation, "PendingIntent cannot be null.");
+ PendingIntent operation, @NonNull String callingPackageName) {
+ Objects.requireNonNull(operation, "PendingIntent cannot be null.");
+ final int callingUid = Binder.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
-
+ Binder.getCallingPid(), callingUid, callingPackageName);
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
- restrictRequestUidsForCaller(nc);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -5324,6 +5725,11 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
}
+ /** Returns the next Network provider ID. */
+ public final int nextNetworkProviderId() {
+ return mNextNetworkProviderId.getAndIncrement();
+ }
+
@Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
ensureNetworkRequestHasType(networkRequest);
@@ -5333,52 +5739,79 @@
@Override
public int registerNetworkFactory(Messenger messenger, String name) {
- enforceConnectivityInternalPermission();
- NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel(),
- NetworkFactory.SerialNumber.nextSerialNumber());
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
- return nfi.factorySerialNumber;
+ enforceNetworkFactoryPermission();
+ NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
+ nextNetworkProviderId(), null /* deathRecipient */);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+ return npi.providerId;
}
- private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
- if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
- mNetworkFactoryInfos.put(nfi.messenger, nfi);
- nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
+ private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
+ if (mNetworkProviderInfos.containsKey(npi.messenger)) {
+ // Avoid creating duplicates. even if an app makes a direct AIDL call.
+ // This will never happen if an app calls ConnectivityManager#registerNetworkProvider,
+ // as that will throw if a duplicate provider is registered.
+ Slog.e(TAG, "Attempt to register existing NetworkProviderInfo "
+ + mNetworkProviderInfos.get(npi.messenger).name);
+ return;
+ }
+
+ if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
+ mNetworkProviderInfos.put(npi.messenger, npi);
+ npi.connect(mContext, mTrackerHandler);
+ if (!npi.isLegacyNetworkFactory()) {
+ // Legacy NetworkFactories get their requests when their AsyncChannel connects.
+ sendAllRequestsToProvider(npi);
+ }
+ }
+
+ @Override
+ public int registerNetworkProvider(Messenger messenger, String name) {
+ enforceNetworkFactoryOrSettingsPermission();
+ NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
+ null /* asyncChannel */, nextNetworkProviderId(),
+ () -> unregisterNetworkProvider(messenger));
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
+ return npi.providerId;
+ }
+
+ @Override
+ public void unregisterNetworkProvider(Messenger messenger) {
+ enforceNetworkFactoryOrSettingsPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
@Override
public void unregisterNetworkFactory(Messenger messenger) {
- enforceConnectivityInternalPermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
+ unregisterNetworkProvider(messenger);
}
- private void handleUnregisterNetworkFactory(Messenger messenger) {
- NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
- if (nfi == null) {
- loge("Failed to find Messenger in unregisterNetworkFactory");
+ private void handleUnregisterNetworkProvider(Messenger messenger) {
+ NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
+ if (npi == null) {
+ loge("Failed to find Messenger in unregisterNetworkProvider");
return;
}
- if (DBG) log("unregisterNetworkFactory for " + nfi.name);
+ if (DBG) log("unregisterNetworkProvider for " + npi.name);
}
- /**
- * NetworkAgentInfo supporting a request by requestId.
- * These have already been vetted (their Capabilities satisfy the request)
- * and the are the highest scored network available.
- * the are keyed off the Requests requestId.
- */
- // NOTE: Accessed on multiple threads, must be synchronized on itself.
- @GuardedBy("mNetworkForRequestId")
- private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = new SparseArray<>();
+ @Override
+ public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ if (request.hasTransport(TRANSPORT_TEST)) {
+ enforceNetworkFactoryOrTestNetworksPermission();
+ } else {
+ enforceNetworkFactoryPermission();
+ }
+ mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
+ }
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@GuardedBy("mNetworkForNetId")
private final SparseArray<NetworkAgentInfo> mNetworkForNetId = new SparseArray<>();
// NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
- // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+ // An entry is first reserved with NetIdManager, prior to being added to mNetworkForNetId, so
// there may not be a strict 1:1 correlation between the two.
- @GuardedBy("mNetworkForNetId")
- private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+ private final NetIdManager mNetIdManager;
// NetworkAgentInfo keyed off its connecting messenger
// TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
@@ -5389,7 +5822,11 @@
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
// Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
+ @NonNull
private final NetworkRequest mDefaultRequest;
+ // The NetworkAgentInfo currently satisfying the default request, if any.
+ @Nullable
+ private volatile NetworkAgentInfo mDefaultNetworkNai = null;
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
@@ -5399,26 +5836,8 @@
// priority networks like ethernet are active.
private final NetworkRequest mDefaultWifiRequest;
- private NetworkAgentInfo getNetworkForRequest(int requestId) {
- synchronized (mNetworkForRequestId) {
- return mNetworkForRequestId.get(requestId);
- }
- }
-
- private void clearNetworkForRequest(int requestId) {
- synchronized (mNetworkForRequestId) {
- mNetworkForRequestId.remove(requestId);
- }
- }
-
- private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
- synchronized (mNetworkForRequestId) {
- mNetworkForRequestId.put(requestId, nai);
- }
- }
-
private NetworkAgentInfo getDefaultNetwork() {
- return getNetworkForRequest(mDefaultRequest.requestId);
+ return mDefaultNetworkNai;
}
@Nullable
@@ -5447,11 +5866,14 @@
// changes that would conflict throughout the automerger graph. Having this method temporarily
// helps with the process of going through with all these dependent changes across the entire
// tree.
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ /**
+ * Register a new agent. {@see #registerNetworkAgent} below.
+ */
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc) {
+ int currentScore, NetworkAgentConfig networkAgentConfig) {
return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
- currentScore, networkMisc, NetworkFactory.SerialNumber.NONE);
+ currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
}
/**
@@ -5463,35 +5885,48 @@
* @param linkProperties the initial link properties of this network. They can be updated
* later : see {@link #updateLinkProperties}.
* @param networkCapabilities the initial capabilites of this network. They can be updated
- * later : see {@link #updateNetworkCapabilities}.
+ * later : see {@link #updateCapabilities}.
* @param currentScore the initial score of the network. See
* {@link NetworkAgentInfo#getCurrentScore}.
- * @param networkMisc metadata about the network. This is never updated.
- * @param factorySerialNumber the serial number of the factory owning this NetworkAgent.
+ * @param networkAgentConfig metadata about the network. This is never updated.
+ * @param providerId the ID of the provider owning this NetworkAgent.
+ * @return the network created for this agent.
*/
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc, int factorySerialNumber) {
- enforceConnectivityInternalPermission();
+ int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+ enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+ // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+ // sees capabilities that may be malicious, which might prevent mistakes in the future.
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid());
+ } else {
+ enforceNetworkFactoryPermission();
+ }
LinkProperties lp = new LinkProperties(linkProperties);
- lp.ensureDirectlyConnectedRoutes();
+
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
- mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
- mNMS, factorySerialNumber);
- // Make sure the network capabilities reflect what the agent info says.
- nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
+ new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
+ currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
+ this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
+
+ // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
+ nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
+ processLinkPropertiesFromAgent(nai, nai.linkProperties);
+
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
- ? nai.networkCapabilities.getSSID() : extraInfo;
+ ? nai.networkCapabilities.getSsid() : extraInfo;
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- getNetworkStack().makeNetworkMonitor(
+ mDeps.getNetworkStack().makeNetworkMonitor(
nai.network, name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5500,12 +5935,7 @@
// If the network disconnects or sends any other event before that, messages are deferred by
// NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
// registration.
- return nai.network.netId;
- }
-
- @VisibleForTesting
- protected NetworkStackClient getNetworkStack() {
- return NetworkStackClient.getInstance();
+ return nai.network;
}
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
@@ -5523,21 +5953,26 @@
}
nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
NetworkInfo networkInfo = nai.networkInfo;
- nai.networkInfo = null;
updateNetworkInfo(nai, networkInfo);
updateUids(nai, null, nai.networkCapabilities);
}
+ private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
+ lp.ensureDirectlyConnectedRoutes();
+ nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
+ }
+
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
- LinkProperties oldLp) {
+ @NonNull LinkProperties oldLp) {
int netId = networkAgent.network.netId;
- // The NetworkAgentInfo does not know whether clatd is running on its network or not, or
- // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties
- // are accurate.
+ // The NetworkAgent does not know whether clatd is running on its network or not, or whether
+ // a NAT64 prefix was discovered by the DNS resolver. Before we do anything else, make sure
+ // the LinkProperties for the network are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
+ updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
+ networkAgent.networkInfo.getType());
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
@@ -5565,6 +6000,13 @@
} else {
updateProxy(newLp, oldLp);
}
+
+ updateWakeOnLan(newLp);
+
+ // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
+ // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
+ newLp.setCaptivePortalData(networkAgent.captivePortalData);
+
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
synchronized (networkAgent) {
@@ -5573,7 +6015,8 @@
// Start or stop DNS64 detection and 464xlat according to network state.
networkAgent.clatd.update();
notifyIfacesChangedForNetworkStats();
- networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
+ networkAgent.networkMonitor().notifyLinkPropertiesChanged(
+ new LinkProperties(newLp, true /* parcelSensitiveFields */));
if (networkAgent.everConnected) {
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
@@ -5613,21 +6056,26 @@
}
- private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
- NetworkCapabilities caps) {
- CompareResult<String> interfaceDiff = new CompareResult<>(
+ private void updateInterfaces(final @Nullable LinkProperties newLp,
+ final @Nullable LinkProperties oldLp, final int netId,
+ final @Nullable NetworkCapabilities caps, final int legacyType) {
+ final CompareResult<String> interfaceDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllInterfaceNames() : null,
newLp != null ? newLp.getAllInterfaceNames() : null);
- for (String iface : interfaceDiff.added) {
- try {
- if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNMS.addInterfaceToNetwork(iface, netId);
- wakeupModifyInterface(iface, caps, true);
- } catch (Exception e) {
- loge("Exception adding interface: " + e);
+ if (!interfaceDiff.added.isEmpty()) {
+ final IBatteryStats bs = mDeps.getBatteryStatsService();
+ for (final String iface : interfaceDiff.added) {
+ try {
+ if (DBG) log("Adding iface " + iface + " to network " + netId);
+ mNMS.addInterfaceToNetwork(iface, netId);
+ wakeupModifyInterface(iface, caps, true);
+ bs.noteNetworkInterfaceType(iface, legacyType);
+ } catch (Exception e) {
+ loge("Exception adding interface: " + e);
+ }
}
}
- for (String iface : interfaceDiff.removed) {
+ for (final String iface : interfaceDiff.removed) {
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
@@ -5638,15 +6086,49 @@
}
}
+ // TODO: move to frameworks/libs/net.
+ private RouteInfoParcel convertRouteInfo(RouteInfo route) {
+ final String nextHop;
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ nextHop = route.getGateway().getHostAddress();
+ } else {
+ nextHop = INetd.NEXTHOP_NONE;
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ nextHop = INetd.NEXTHOP_UNREACHABLE;
+ break;
+ case RouteInfo.RTN_THROW:
+ nextHop = INetd.NEXTHOP_THROW;
+ break;
+ default:
+ nextHop = INetd.NEXTHOP_NONE;
+ break;
+ }
+
+ final RouteInfoParcel rip = new RouteInfoParcel();
+ rip.ifName = route.getInterface();
+ rip.destination = route.getDestination().toString();
+ rip.nextHop = nextHop;
+ rip.mtu = route.getMtu();
+
+ return rip;
+ }
+
/**
* Have netd update routes from oldLp to newLp.
* @return true if routes changed between oldLp and newLp
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
- // Compare the route diff to determine which routes should be added and removed.
- CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(
- oldLp != null ? oldLp.getAllRoutes() : null,
- newLp != null ? newLp.getAllRoutes() : null);
+ // compare the route diff to determine which routes have been updated
+ final CompareOrUpdateResult<RouteInfo.RouteKey, RouteInfo> routeDiff =
+ new CompareOrUpdateResult<>(
+ oldLp != null ? oldLp.getAllRoutes() : null,
+ newLp != null ? newLp.getAllRoutes() : null,
+ (r) -> r.getRouteKey());
// add routes before removing old in case it helps with continuous connectivity
@@ -5655,21 +6137,21 @@
if (route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for non-gateway: " + e);
+ loge("Exception in networkAddRouteParcel for non-gateway: " + e);
}
}
}
for (RouteInfo route : routeDiff.added) {
- if (route.hasGateway() == false) continue;
+ if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for gateway: " + e);
+ loge("Exception in networkAddRouteParcel for gateway: " + e);
}
}
}
@@ -5677,12 +6159,22 @@
for (RouteInfo route : routeDiff.removed) {
if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNMS.removeRoute(netId, route);
+ mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
- loge("Exception in removeRoute: " + e);
+ loge("Exception in networkRemoveRouteParcel: " + e);
}
}
- return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
+
+ for (RouteInfo route : routeDiff.updated) {
+ if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
+ try {
+ mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
+ } catch (Exception e) {
+ loge("Exception in networkUpdateRouteParcel: " + e);
+ }
+ }
+ return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
+ || !routeDiff.updated.isEmpty();
}
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
@@ -5698,7 +6190,13 @@
log("Setting DNS servers for network " + netId + " to " + dnses);
}
try {
- mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
+ mDnsManager.noteDnsServersForNetwork(netId, newLp);
+ // TODO: netd should listen on [::1]:53 and proxy queries to the current
+ // default network, and we should just set net.dns1 to ::1, not least
+ // because applications attempting to use net.dns resolvers will bypass
+ // the privacy protections of things like DNS-over-TLS.
+ if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
}
@@ -5722,7 +6220,7 @@
}
final Set<UidRange> ranges = nai.networkCapabilities.getUids();
- final int vpnAppUid = nai.networkCapabilities.getEstablishingVpnAppUid();
+ final int vpnAppUid = nai.networkCapabilities.getOwnerUid();
// TODO: this create a window of opportunity for apps to receive traffic between the time
// when the old rules are removed and the time when new rules are added. To fix this,
// make eBPF support two whitelisted interfaces so here new rules can be added before the
@@ -5735,6 +6233,10 @@
}
}
+ private void updateWakeOnLan(@NonNull LinkProperties lp) {
+ lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
+ }
+
private int getNetworkPermission(NetworkCapabilities nc) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
return INetd.PERMISSION_SYSTEM;
@@ -5745,6 +6247,19 @@
return INetd.PERMISSION_NONE;
}
+ private void updateNetworkPermissions(@NonNull final NetworkAgentInfo nai,
+ @NonNull final NetworkCapabilities newNc) {
+ final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+ final int newPermission = getNetworkPermission(newNc);
+ if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
+ try {
+ mNMS.setNetworkPermission(nai.network.netId, newPermission);
+ } catch (RemoteException e) {
+ loge("Exception in setNetworkPermission: " + e);
+ }
+ }
+ }
+
/**
* Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
@@ -5754,7 +6269,7 @@
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
// Don't complain for VPNs since they're not driven by requests and there is no risk of
// causing a connect/teardown loop.
- // TODO: remove this altogether and make it the responsibility of the NetworkFactories to
+ // TODO: remove this altogether and make it the responsibility of the NetworkProviders to
// avoid connect/teardown loops.
if (nai.everConnected &&
!nai.isVPN() &&
@@ -5785,16 +6300,18 @@
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (nai.isSuspended()) {
- newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- } else {
- newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- }
if (nai.partialConnectivity) {
newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
} else {
newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
}
+ newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
+
+ // TODO : remove this once all factories are updated to send NOT_SUSPENDED and NOT_ROAMING
+ if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
return newNc;
}
@@ -5815,26 +6332,12 @@
* @param nai the network having its capabilities updated.
* @param nc the new network capabilities.
*/
- private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+ private void updateCapabilities(final int oldScore, @NonNull final NetworkAgentInfo nai,
+ @NonNull final NetworkCapabilities nc) {
NetworkCapabilities newNc = mixInCapabilities(nai, nc);
-
if (Objects.equals(nai.networkCapabilities, newNc)) return;
-
- final int oldPermission = getNetworkPermission(nai.networkCapabilities);
- final int newPermission = getNetworkPermission(newNc);
- if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
- try {
- mNMS.setNetworkPermission(nai.network.netId, newPermission);
- } catch (RemoteException e) {
- loge("Exception in setNetworkPermission: " + e);
- }
- }
-
- final NetworkCapabilities prevNc;
- synchronized (nai) {
- prevNc = nai.networkCapabilities;
- nai.setNetworkCapabilities(newNc);
- }
+ updateNetworkPermissions(nai, newNc);
+ final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
updateUids(nai, prevNc, newNc);
@@ -5843,14 +6346,30 @@
// the change we're processing can't affect any requests, it can only affect the listens
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
- processListenRequests(nai, true);
+ processListenRequests(nai);
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
- rematchAllNetworksAndRequests(nai, oldScore);
+ rematchAllNetworksAndRequests();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+ // never returns null), so mark the relevant members and functions in nai as @NonNull and
+ // remove this test
if (prevNc != null) {
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -5875,6 +6394,10 @@
// bubble those changes through.
updateAllVpnsCapabilities();
}
+
+ if (!newNc.equalsTransportTypes(prevNc)) {
+ mDnsManager.updateTransportsForNetwork(nai.network.netId, newNc.getTransportTypes());
+ }
}
/**
@@ -5892,17 +6415,18 @@
* 3. the VPN is fully-routed
* 4. the VPN interface is non-null
*
- * @See INetd#firewallAddUidInterfaceRules
- * @See INetd#firewallRemoveUidInterfaceRules
+ * @see INetd#firewallAddUidInterfaceRules
+ * @see INetd#firewallRemoveUidInterfaceRules
*/
private boolean requiresVpnIsolation(@NonNull NetworkAgentInfo nai, NetworkCapabilities nc,
LinkProperties lp) {
if (nc == null || lp == null) return false;
return nai.isVPN()
- && !nai.networkMisc.allowBypass
- && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
+ && !nai.networkAgentConfig.allowBypass
+ && nc.getOwnerUid() != Process.SYSTEM_UID
&& lp.getInterfaceName() != null
- && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
+ && (lp.hasIPv4DefaultRoute() || lp.hasIpv4UnreachableDefaultRoute())
+ && (lp.hasIPv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
}
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
@@ -5937,7 +6461,7 @@
final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
final String iface = nai.linkProperties.getInterfaceName();
// For VPN uid interface filtering, old ranges need to be removed before new ranges can
- // be added, due to the range being expanded and stored as invidiual UIDs. For example
+ // be added, due to the range being expanded and stored as individual UIDs. For example
// the UIDs might be updated from [0, 99999] to ([0, 10012], [10014, 99999]) which means
// prevRanges = [0, 99999] while newRanges = [0, 10012], [10014, 99999]. If prevRanges
// were added first and then newRanges got removed later, there would be only one uid
@@ -5948,12 +6472,10 @@
// TODO Fix this window by computing an accurate diff on Set<UidRange>, so the old range
// to be removed will never overlap with the new range to be added.
if (wasFiltering && !prevRanges.isEmpty()) {
- mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges,
- prevNc.getEstablishingVpnAppUid());
+ mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, prevNc.getOwnerUid());
}
if (shouldFilter && !newRanges.isEmpty()) {
- mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges,
- newNc.getEstablishingVpnAppUid());
+ mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, newNc.getOwnerUid());
}
} catch (Exception e) {
// Never crash!
@@ -5968,13 +6490,13 @@
// Ignore updates for disconnected networks
return;
}
- // newLp is already a defensive copy.
- newLp.ensureDirectlyConnectedRoutes();
if (VDBG || DDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created +
- "; everConnected=" + nai.everConnected);
+ log("Update of LinkProperties for " + nai.toShortString()
+ + "; created=" + nai.created
+ + "; everConnected=" + nai.everConnected);
}
+ // TODO: eliminate this defensive copy after confirming that updateLinkProperties does not
+ // modify its oldLp parameter.
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
@@ -5987,19 +6509,41 @@
}
}
- private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, NetworkAgentInfo nai) {
- int score = 0;
- int serial = 0;
+ private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
+ @Nullable NetworkAgentInfo nai) {
+ final int score;
+ final int serial;
if (nai != null) {
score = nai.getCurrentScore();
serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = 0;
}
if (VDBG || DDBG){
log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
}
- for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
- nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- serial, networkRequest);
+ for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.requestNetwork(networkRequest, score, serial);
+ }
+ }
+
+ /** Sends all current NetworkRequests to the specified factory. */
+ private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ ensureRunningOnConnectivityServiceThread();
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.request.isListen()) continue;
+ NetworkAgentInfo nai = nri.mSatisfier;
+ final int score;
+ final int serial;
+ if (nai != null) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6041,7 +6585,10 @@
private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) {
- return; // Default request has no msgr
+ // Default request has no msgr. Also prevents callbacks from being invoked for
+ // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
+ // are Type.LISTEN, but should not have NetworkCallbacks invoked.
+ return;
}
Bundle bundle = new Bundle();
// TODO: check if defensive copies of data is needed.
@@ -6052,9 +6599,15 @@
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_AVAILABLE: {
- putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid));
- putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ final NetworkCapabilities nc =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ nc, nri.mUid, nri.request.getRequestorPackageName()));
+ putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+ networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
msg.arg1 = arg1;
break;
@@ -6065,13 +6618,18 @@
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
- final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid);
- putParcelable(bundle, nc);
+ final NetworkCapabilities netCap =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ netCap, nri.mUid, nri.request.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
- putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+ networkAgent.linkProperties, nri.mPid, nri.mUid));
break;
}
case ConnectivityManager.CALLBACK_BLK_CHANGED: {
@@ -6116,7 +6674,7 @@
loge("Unknown NetworkAgentInfo in handleLingerComplete");
return;
}
- if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+ if (DBG) log("handleLingerComplete for " + oldNetwork.toShortString());
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
@@ -6132,26 +6690,41 @@
}
}
- private void makeDefault(NetworkAgentInfo newNetwork) {
+ private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
+ mDefaultNetworkNai = newNetwork;
+
try {
- mNMS.setDefaultNetId(newNetwork.network.netId);
+ if (null != newNetwork) {
+ mNMS.setDefaultNetId(newNetwork.network.netId);
+ } else {
+ mNMS.clearDefaultNetId();
+ }
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
notifyLockdownVpn(newNetwork);
- handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
- updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
- mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+ handleApplyDefaultProxy(null != newNetwork
+ ? newNetwork.linkProperties.getHttpProxy() : null);
+ updateTcpBufferSizes(null != newNetwork
+ ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+ mDnsManager.setDefaultDnsSystemProperties(null != newNetwork
+ ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
updateAllVpnsCapabilities();
}
- private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
+ private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
// For consistency with previous behaviour, send onLost callbacks before onAvailable.
+ processNewlyLostListenRequests(nai);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ processNewlySatisfiedListenRequests(nai);
+ }
+
+ private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkRequest nr = nri.request;
if (!nr.isListen()) continue;
@@ -6160,11 +6733,9 @@
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
+ }
- if (capabilitiesChanged) {
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
+ private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkRequest nr = nri.request;
if (!nr.isListen()) continue;
@@ -6175,311 +6746,347 @@
}
}
- // Handles a network appearing or improving its score.
- //
- // - Evaluates all current NetworkRequests that can be
- // satisfied by newNetwork, and reassigns to newNetwork
- // any such requests for which newNetwork is the best.
- //
- // - Lingers any validated Networks that as a result are no longer
- // needed. A network is needed if it is the best network for
- // one or more NetworkRequests, or if it is a VPN.
- //
- // - Tears down newNetwork if it just became validated
- // but turns out to be unneeded.
- //
- // - If reapUnvalidatedNetworks==REAP, tears down unvalidated
- // networks that have no chance (i.e. even if validated)
- // of becoming the highest scoring network.
- //
- // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
- // it does not remove NetworkRequests that other Networks could better satisfy.
- // If you need to handle decreases in score, use {@link rematchAllNetworksAndRequests}.
- // This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
- // as it performs better by a factor of the number of Networks.
- //
- // @param newNetwork is the network to be matched against NetworkRequests.
- // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be
- // performed to tear down unvalidated networks that have no chance (i.e. even if
- // validated) of becoming the highest scoring network.
- private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
- ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
- if (!newNetwork.everConnected) return;
- boolean keep = newNetwork.isVPN();
- boolean isNewDefault = false;
- NetworkAgentInfo oldDefaultNetwork = null;
-
- final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
- final int score = newNetwork.getCurrentScore();
-
- if (VDBG || DDBG) log("rematching " + newNetwork.name());
-
- // Find and migrate to this Network any NetworkRequests for
- // which this network is now the best.
- ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<>();
- ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
- NetworkCapabilities nc = newNetwork.networkCapabilities;
- if (VDBG) log(" network has: " + nc);
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- // Process requests in the first pass and listens in the second pass. This allows us to
- // change a network's capabilities depending on which requests it has. This is only
- // correct if the change in capabilities doesn't affect whether the network satisfies
- // requests or not, and doesn't affect the network's score.
- if (nri.request.isListen()) continue;
-
- final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId);
- final boolean satisfies = newNetwork.satisfies(nri.request);
- if (newNetwork == currentNetwork && satisfies) {
- if (VDBG) {
- log("Network " + newNetwork.name() + " was already satisfying" +
- " request " + nri.request.requestId + ". No change.");
- }
- keep = true;
- continue;
+ // An accumulator class to gather the list of changes that result from a rematch.
+ private static class NetworkReassignment {
+ static class RequestReassignment {
+ @NonNull public final NetworkRequestInfo mRequest;
+ @Nullable public final NetworkAgentInfo mOldNetwork;
+ @Nullable public final NetworkAgentInfo mNewNetwork;
+ RequestReassignment(@NonNull final NetworkRequestInfo request,
+ @Nullable final NetworkAgentInfo oldNetwork,
+ @Nullable final NetworkAgentInfo newNetwork) {
+ mRequest = request;
+ mOldNetwork = oldNetwork;
+ mNewNetwork = newNetwork;
}
- // check if it satisfies the NetworkCapabilities
- if (VDBG) log(" checking if request is satisfied: " + nri.request);
- if (satisfies) {
- // next check if it's better than any current network we're using for
- // this request
- if (VDBG || DDBG) {
- log("currentScore = " +
- (currentNetwork != null ? currentNetwork.getCurrentScore() : 0) +
- ", newScore = " + score);
- }
- if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
- if (VDBG) log("rematch for " + newNetwork.name());
- if (currentNetwork != null) {
- if (VDBG || DDBG){
- log(" accepting network in place of " + currentNetwork.name());
- }
- currentNetwork.removeRequest(nri.request.requestId);
- currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
- affectedNetworks.add(currentNetwork);
- } else {
- if (VDBG || DDBG) log(" accepting network in place of null");
- }
- newNetwork.unlingerRequest(nri.request);
- setNetworkForRequest(nri.request.requestId, newNetwork);
- if (!newNetwork.addRequest(nri.request)) {
- Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
- }
- addedRequests.add(nri);
- keep = true;
- // Tell NetworkFactories about the new score, so they can stop
- // trying to connect if they know they cannot match it.
- // TODO - this could get expensive if we have a lot of requests for this
- // network. Think about if there is a way to reduce this. Push
- // netid->request mapping to each factory?
- sendUpdatedScoreToFactories(nri.request, newNetwork);
- if (isDefaultRequest(nri)) {
- isNewDefault = true;
- oldDefaultNetwork = currentNetwork;
- if (currentNetwork != null) {
- mLingerMonitor.noteLingerDefaultNetwork(currentNetwork, newNetwork);
- }
- }
- }
- } else if (newNetwork.isSatisfyingRequest(nri.request.requestId)) {
- // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
- // mark it as no longer satisfying "nri". Because networks are processed by
- // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
- // match "newNetwork" before this loop will encounter a "currentNetwork" with higher
- // score than "newNetwork" and where "currentNetwork" no longer satisfies "nri".
- // This means this code doesn't have to handle the case where "currentNetwork" no
- // longer satisfies "nri" when "currentNetwork" does not equal "newNetwork".
- if (DBG) {
- log("Network " + newNetwork.name() + " stopped satisfying" +
- " request " + nri.request.requestId);
- }
- newNetwork.removeRequest(nri.request.requestId);
- if (currentNetwork == newNetwork) {
- clearNetworkForRequest(nri.request.requestId);
- sendUpdatedScoreToFactories(nri.request, null);
- } else {
- Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
- newNetwork.name() +
- " without updating mNetworkForRequestId or factories!");
- }
- // TODO: Technically, sending CALLBACK_LOST here is
- // incorrect if there is a replacement network currently
- // connected that can satisfy nri, which is a request
- // (not a listen). However, the only capability that can both
- // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
- // so this code is only incorrect for a network that loses
- // the TRUSTED capability, which is a rare case.
- callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
+ public String toString() {
+ return mRequest.request.requestId + " : "
+ + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+ + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
}
}
- if (isNewDefault) {
- updateDataActivityTracking(newNetwork, oldDefaultNetwork);
- // Notify system services that this network is up.
- makeDefault(newNetwork);
+
+ @NonNull private final ArrayList<RequestReassignment> mReassignments = new ArrayList<>();
+
+ @NonNull Iterable<RequestReassignment> getRequestReassignments() {
+ return mReassignments;
+ }
+
+ void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
+ if (!Build.IS_USER) {
+ // The code is never supposed to add two reassignments of the same request. Make
+ // sure this stays true, but without imposing this expensive check on all
+ // reassignments on all user devices.
+ for (final RequestReassignment existing : mReassignments) {
+ if (existing.mRequest.equals(reassignment.mRequest)) {
+ throw new IllegalStateException("Trying to reassign ["
+ + reassignment + "] but already have ["
+ + existing + "]");
+ }
+ }
+ }
+ mReassignments.add(reassignment);
+ }
+
+ // Will return null if this reassignment does not change the network assigned to
+ // the passed request.
+ @Nullable
+ private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
+ for (final RequestReassignment event : getRequestReassignments()) {
+ if (nri == event.mRequest) return event;
+ }
+ return null;
+ }
+
+ public String toString() {
+ final StringJoiner sj = new StringJoiner(", " /* delimiter */,
+ "NetReassign [" /* prefix */, "]" /* suffix */);
+ if (mReassignments.isEmpty()) return sj.add("no changes").toString();
+ for (final RequestReassignment rr : getRequestReassignments()) {
+ sj.add(rr.toString());
+ }
+ return sj.toString();
+ }
+
+ public String debugString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("NetworkReassignment :");
+ if (mReassignments.isEmpty()) return sb.append(" no changes").toString();
+ for (final RequestReassignment rr : getRequestReassignments()) {
+ sb.append("\n ").append(rr);
+ }
+ return sb.append("\n").toString();
+ }
+ }
+
+ private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo previousSatisfier,
+ @Nullable final NetworkAgentInfo newSatisfier,
+ final long now) {
+ if (newSatisfier != null) {
+ if (VDBG) log("rematch for " + newSatisfier.toShortString());
+ if (previousSatisfier != null) {
+ if (VDBG || DDBG) {
+ log(" accepting network in place of " + previousSatisfier.toShortString());
+ }
+ previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+ } else {
+ if (VDBG || DDBG) log(" accepting network in place of null");
+ }
+ newSatisfier.unlingerRequest(nri.request);
+ if (!newSatisfier.addRequest(nri.request)) {
+ Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ + nri.request);
+ }
+ } else {
+ if (DBG) {
+ log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
+ + " request " + nri.request.requestId);
+ }
+ previousSatisfier.removeRequest(nri.request.requestId);
+ }
+ nri.mSatisfier = newSatisfier;
+ }
+
+ @NonNull
+ private NetworkReassignment computeNetworkReassignment() {
+ ensureRunningOnConnectivityServiceThread();
+ final NetworkReassignment changes = new NetworkReassignment();
+
+ // Gather the list of all relevant agents and sort them by score.
+ final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (!nai.everConnected) continue;
+ nais.add(nai);
+ }
+
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.request.isListen()) continue;
+ final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+ if (bestNetwork != nri.mSatisfier) {
+ // bestNetwork may be null if no network can satisfy this request.
+ changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+ nri, nri.mSatisfier, bestNetwork));
+ }
+ }
+ return changes;
+ }
+
+ /**
+ * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchAllNetworksAndRequests() {
+ // TODO: This may be slow, and should be optimized.
+ final long now = SystemClock.elapsedRealtime();
+ final NetworkReassignment changes = computeNetworkReassignment();
+ if (VDBG || DDBG) {
+ log(changes.debugString());
+ } else if (DBG) {
+ log(changes.toString()); // Shorter form, only one line of log
+ }
+ applyNetworkReassignment(changes, now);
+ }
+
+ private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
+ final long now) {
+ final Collection<NetworkAgentInfo> nais = mNetworkAgentInfos.values();
+
+ // Since most of the time there are only 0 or 1 background networks, it would probably
+ // be more efficient to just use an ArrayList here. TODO : measure performance
+ final ArraySet<NetworkAgentInfo> oldBgNetworks = new ArraySet<>();
+ for (final NetworkAgentInfo nai : nais) {
+ if (nai.isBackgroundNetwork()) oldBgNetworks.add(nai);
+ }
+
+ // First, update the lists of satisfied requests in the network agents. This is necessary
+ // because some code later depends on this state to be correct, most prominently computing
+ // the linger status.
+ for (final NetworkReassignment.RequestReassignment event :
+ changes.getRequestReassignments()) {
+ updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
+ event.mNewNetwork, now);
+ }
+
+ final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+ final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
+ final NetworkReassignment.RequestReassignment reassignment =
+ changes.getReassignment(defaultRequestInfo);
+ final NetworkAgentInfo newDefaultNetwork =
+ null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
+
+ if (oldDefaultNetwork != newDefaultNetwork) {
+ if (oldDefaultNetwork != null) {
+ mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+ }
+ updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+ // Notify system services of the new default.
+ makeDefault(newDefaultNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
- now, newNetwork, oldDefaultNetwork);
+ mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+ now, newDefaultNetwork, oldDefaultNetwork);
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
- if (!newNetwork.networkCapabilities.equalRequestableCapabilities(nc)) {
- Slog.wtf(TAG, String.format(
- "BUG: %s changed requestable capabilities during rematch: %s -> %s",
- newNetwork.name(), nc, newNetwork.networkCapabilities));
- }
- if (newNetwork.getCurrentScore() != score) {
- Slog.wtf(TAG, String.format(
- "BUG: %s changed score during rematch: %d -> %d",
- newNetwork.name(), score, newNetwork.getCurrentScore()));
- }
-
- // Second pass: process all listens.
- if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
- // If the network went from background to foreground or vice versa, we need to update
- // its foreground state. It is safe to do this after rematching the requests because
- // NET_CAPABILITY_FOREGROUND does not affect requests, as is not a requestable
- // capability and does not affect the network's score (see the Slog.wtf call above).
- updateCapabilities(score, newNetwork, newNetwork.networkCapabilities);
- } else {
- processListenRequests(newNetwork, false);
- }
-
- // do this after the default net is switched, but
+ // Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
- for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
+ for (final NetworkReassignment.RequestReassignment event :
+ changes.getRequestReassignments()) {
+ // Tell NetworkProviders about the new score, so they can stop
+ // trying to connect if they know they cannot match it.
+ // TODO - this could get expensive if there are a lot of outstanding requests for this
+ // network. Think of a way to reduce this. Push netid->request mapping to each factory?
+ sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
- // Linger any networks that are no longer needed. This should be done after sending the
- // available callback for newNetwork.
- for (NetworkAgentInfo nai : affectedNetworks) {
- updateLingerState(nai, now);
- }
- // Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
- // does not need to be done in any particular order.
- updateLingerState(newNetwork, now);
-
- if (isNewDefault) {
- // Maintain the illusion: since the legacy API only
- // understands one network at a time, we must pretend
- // that the current default network disconnected before
- // the new one connected.
- if (oldDefaultNetwork != null) {
- mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
- oldDefaultNetwork, true);
- }
- mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
- mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
- notifyLockdownVpn(newNetwork);
- }
-
- if (keep) {
- // Notify battery stats service about this network, both the normal
- // interface and any stacked links.
- // TODO: Avoid redoing this; this must only be done once when a network comes online.
- try {
- final IBatteryStats bs = BatteryStatsService.getService();
- final int type = newNetwork.networkInfo.getType();
-
- final String baseIface = newNetwork.linkProperties.getInterfaceName();
- bs.noteNetworkInterfaceType(baseIface, type);
- for (LinkProperties stacked : newNetwork.linkProperties.getStackedLinks()) {
- final String stackedIface = stacked.getInterfaceName();
- bs.noteNetworkInterfaceType(stackedIface, type);
- }
- } catch (RemoteException ignored) {
- }
-
- // This has to happen after the notifyNetworkCallbacks as that tickles each
- // ConnectivityManager instance so that legacy requests correctly bind dns
- // requests to this network. The legacy users are listening for this broadcast
- // and will generally do a dns request so they can ensureRouteToHost and if
- // they do that before the callbacks happen they'll use the default network.
- //
- // TODO: Is there still a race here? We send the broadcast
- // after sending the callback, but if the app can receive the
- // broadcast before the callback, it might still break.
- //
- // This *does* introduce a race where if the user uses the new api
- // (notification callbacks) and then uses the old api (getNetworkInfo(type))
- // they may get old info. Reverse this after the old startUsing api is removed.
- // This is on top of the multiple intent sequencing referenced in the todo above.
- for (int i = 0; i < newNetwork.numNetworkRequests(); i++) {
- NetworkRequest nr = newNetwork.requestAt(i);
- if (nr.legacyType != TYPE_NONE && nr.isRequest()) {
- // legacy type tracker filters out repeat adds
- mLegacyTypeTracker.add(nr.legacyType, newNetwork);
- }
- }
-
- // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
- // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
- // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
- // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
- if (newNetwork.isVPN()) {
- mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
+ if (null != event.mNewNetwork) {
+ notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ } else {
+ callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ ConnectivityManager.CALLBACK_LOST, 0);
}
}
- if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (nai.getLingerExpiry() > 0) {
- // This network has active linger timers and no requests, but is not
- // lingering. Linger it.
- //
- // One way (the only way?) this can happen if this network is unvalidated
- // and became unneeded due to another network improving its score to the
- // point where this network will no longer be able to satisfy any requests
- // even if it validates.
- updateLingerState(nai, now);
- } else {
- if (DBG) log("Reaping " + nai.name());
- teardownUnneededNetwork(nai);
+
+ // Update the linger state before processing listen callbacks, because the background
+ // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // just yet though, because they have to be sent after the listens are processed to keep
+ // backward compatibility.
+ final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+ for (final NetworkAgentInfo nai : nais) {
+ // Rematching may have altered the linger state of some networks, so update all linger
+ // timers. updateLingerState reads the state from the network agent and does nothing
+ // if the state has not changed : the source of truth is controlled with
+ // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
+ // called while rematching the individual networks above.
+ if (updateLingerState(nai, now)) {
+ lingeredNetworks.add(nai);
+ }
+ }
+
+ for (final NetworkAgentInfo nai : nais) {
+ if (!nai.everConnected) continue;
+ final boolean oldBackground = oldBgNetworks.contains(nai);
+ // Process listen requests and update capabilities if the background state has
+ // changed for this network. For consistency with previous behavior, send onLost
+ // callbacks before onAvailable.
+ processNewlyLostListenRequests(nai);
+ if (oldBackground != nai.isBackgroundNetwork()) {
+ applyBackgroundChangeForRematch(nai);
+ }
+ processNewlySatisfiedListenRequests(nai);
+ }
+
+ for (final NetworkAgentInfo nai : lingeredNetworks) {
+ notifyNetworkLosing(nai, now);
+ }
+
+ updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+
+ // Tear down all unneeded networks.
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (nai.getLingerExpiry() > 0) {
+ // This network has active linger timers and no requests, but is not
+ // lingering. Linger it.
+ //
+ // One way (the only way?) this can happen if this network is unvalidated
+ // and became unneeded due to another network improving its score to the
+ // point where this network will no longer be able to satisfy any requests
+ // even if it validates.
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
}
+ } else {
+ if (DBG) log("Reaping " + nai.toShortString());
+ teardownUnneededNetwork(nai);
}
}
}
}
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
- * being disconnected.
- * @param changed If only one Network's score or capabilities have been modified since the last
- * time this function was called, pass this Network in this argument, otherwise pass
- * null.
- * @param oldScore If only one Network has been changed but its NetworkCapabilities have not
- * changed, pass in the Network's score (from getCurrentScore()) prior to the change via
- * this argument, otherwise pass {@code changed.getCurrentScore()} or 0 if
- * {@code changed} is {@code null}. This is because NetworkCapabilities influence a
- * network's score.
+ * Apply a change in background state resulting from rematching networks with requests.
+ *
+ * During rematch, a network may change background states by starting to satisfy or stopping
+ * to satisfy a foreground request. Listens don't count for this. When a network changes
+ * background states, its capabilities need to be updated and callbacks fired for the
+ * capability change.
+ *
+ * @param nai The network that changed background states
*/
- private void rematchAllNetworksAndRequests(NetworkAgentInfo changed, int oldScore) {
- // TODO: This may get slow. The "changed" parameter is provided for future optimization
- // to avoid the slowness. It is not simply enough to process just "changed", for
- // example in the case where "changed"'s score decreases and another network should begin
- // satisfying a NetworkRequest that "changed" currently satisfies.
+ private void applyBackgroundChangeForRematch(@NonNull final NetworkAgentInfo nai) {
+ final NetworkCapabilities newNc = mixInCapabilities(nai, nai.networkCapabilities);
+ if (Objects.equals(nai.networkCapabilities, newNc)) return;
+ updateNetworkPermissions(nai, newNc);
+ nai.getAndSetNetworkCapabilities(newNc);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
- // Optimization: Only reprocess "changed" if its score improved. This is safe because it
- // can only add more NetworkRequests satisfied by "changed", and this is exactly what
- // rematchNetworkAndRequests() handles.
- final long now = SystemClock.elapsedRealtime();
- if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
- } else {
- final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
- new NetworkAgentInfo[mNetworkAgentInfos.size()]);
- // Rematch higher scoring networks first to prevent requests first matching a lower
- // scoring network and then a higher scoring network, which could produce multiple
- // callbacks and inadvertently unlinger networks.
- Arrays.sort(nais);
- for (NetworkAgentInfo nai : nais) {
- rematchNetworkAndRequests(nai,
- // Only reap the last time through the loop. Reaping before all rematching
- // is complete could incorrectly teardown a network that hasn't yet been
- // rematched.
- (nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
- : ReapUnvalidatedNetworks.REAP,
- now);
+ private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork,
+ @NonNull final Collection<NetworkAgentInfo> nais) {
+ if (oldDefaultNetwork != newDefaultNetwork) {
+ // Maintain the illusion : since the legacy API only understands one network at a time,
+ // if the default network changed, apps should see a disconnected broadcast for the
+ // old default network before they see a connected broadcast for the new one.
+ if (oldDefaultNetwork != null) {
+ mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+ oldDefaultNetwork, true);
}
+ if (newDefaultNetwork != null) {
+ // The new default network can be newly null if and only if the old default
+ // network doesn't satisfy the default request any more because it lost a
+ // capability.
+ mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
+ mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+ // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
+ // to reflect the NetworkInfo of this new network. This broadcast has to be sent
+ // after the disconnect broadcasts above, but before the broadcasts sent by the
+ // legacy type tracker below.
+ // TODO : refactor this, it's too complex
+ notifyLockdownVpn(newDefaultNetwork);
+ }
+ }
+
+ // Now that all the callbacks have been sent, send the legacy network broadcasts
+ // as needed. This is necessary so that legacy requests correctly bind dns
+ // requests to this network. The legacy users are listening for this broadcast
+ // and will generally do a dns request so they can ensureRouteToHost and if
+ // they do that before the callbacks happen they'll use the default network.
+ //
+ // TODO: Is there still a race here? The legacy broadcast will be sent after sending
+ // callbacks, but if apps can receive the broadcast before the callback, they still might
+ // have an inconsistent view of networking.
+ //
+ // This *does* introduce a race where if the user uses the new api
+ // (notification callbacks) and then uses the old api (getNetworkInfo(type))
+ // they may get old info. Reverse this after the old startUsing api is removed.
+ // This is on top of the multiple intent sequencing referenced in the todo above.
+ for (NetworkAgentInfo nai : nais) {
+ if (nai.everConnected) {
+ addNetworkToLegacyTypeTracker(nai);
+ }
+ }
+ }
+
+ private void addNetworkToLegacyTypeTracker(@NonNull final NetworkAgentInfo nai) {
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ if (nr.legacyType != TYPE_NONE && nr.isRequest()) {
+ // legacy type tracker filters out repeat adds
+ mLegacyTypeTracker.add(nr.legacyType, nai);
+ }
+ }
+
+ // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
+ // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+ // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
+ // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
+ if (nai.isVPN()) {
+ mLegacyTypeTracker.add(TYPE_VPN, nai);
}
}
@@ -6510,10 +7117,40 @@
}
}
- private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+ @NonNull
+ private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+ final NetworkInfo newInfo = new NetworkInfo(info);
+ // The suspended and roaming bits are managed in NetworkCapabilities.
+ final boolean suspended =
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+ // Only override the state with SUSPENDED if the network is currently in CONNECTED
+ // state. This is because the network could have been suspended before connecting,
+ // or it could be disconnecting while being suspended, and in both these cases
+ // the state should not be overridden. Note that the only detailed state that
+ // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+ // worry about multiple different substates of CONNECTED.
+ newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+ info.getExtraInfo());
+ } else if (!suspended && info.getDetailedState() == NetworkInfo.DetailedState.SUSPENDED) {
+ // SUSPENDED state is currently only overridden from CONNECTED state. In the case the
+ // network agent is created, then goes to suspended, then goes out of suspended without
+ // ever setting connected. Check if network agent is ever connected to update the state.
+ newInfo.setDetailedState(nai.everConnected
+ ? NetworkInfo.DetailedState.CONNECTED
+ : NetworkInfo.DetailedState.CONNECTING,
+ info.getReason(),
+ info.getExtraInfo());
+ }
+ newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ return newInfo;
+ }
+
+ private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+ final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
- final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
@@ -6521,9 +7158,8 @@
notifyLockdownVpn(networkAgent);
if (DBG) {
- log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
- (oldInfo == null ? "null" : oldInfo.getState()) +
- " to " + state);
+ log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+ + oldInfo.getState() + " to " + state);
}
if (!networkAgent.created
@@ -6534,6 +7170,14 @@
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
+ if (networkAgent.isVPN()) {
+ // Initialize the VPN capabilities to their starting values according to the
+ // underlying networks. This will avoid a spurious callback to
+ // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
+ // the VPN would switch from its default, blank capabilities to those
+ // that reflect the capabilities of its underlying networks.
+ updateAllVpnsCapabilities();
+ }
networkAgent.created = true;
}
@@ -6541,14 +7185,13 @@
networkAgent.everConnected = true;
if (networkAgent.linkProperties == null) {
- Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+ Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
}
// NetworkCapabilities need to be set before sending the private DNS config to
// NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
- synchronized (networkAgent) {
- networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
- }
+ networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
+
handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
null);
@@ -6557,11 +7200,13 @@
// command must be sent after updating LinkProperties to maximize chances of
// NetworkMonitor seeing the correct LinkProperties when starting.
// TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
- if (networkAgent.networkMisc.acceptPartialConnectivity) {
+ if (networkAgent.networkAgentConfig.acceptPartialConnectivity) {
networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
networkAgent.networkMonitor().notifyNetworkConnected(
- networkAgent.linkProperties, networkAgent.networkCapabilities);
+ new LinkProperties(networkAgent.linkProperties,
+ true /* parcelSensitiveFields */),
+ networkAgent.networkCapabilities);
scheduleUnvalidatedPrompt(networkAgent);
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
@@ -6578,8 +7223,7 @@
}
// Consider network even though it is not yet validated.
- final long now = SystemClock.elapsedRealtime();
- rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
+ rematchAllNetworksAndRequests();
// This has to happen after matching the requests, because callbacks are just requests.
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
@@ -6596,36 +7240,16 @@
// TODO(b/122649188): send the broadcast only to VPN users.
mProxyTracker.sendProxyBroadcast();
}
- } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
- state == NetworkInfo.State.SUSPENDED) {
- // going into or coming out of SUSPEND: re-score and notify
- if (networkAgent.getCurrentScore() != oldScore) {
- rematchAllNetworksAndRequests(networkAgent, oldScore);
- }
- updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
- networkAgent.networkCapabilities);
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
- ConnectivityManager.CALLBACK_SUSPENDED :
- ConnectivityManager.CALLBACK_RESUMED));
+ } else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
+ state == NetworkInfo.State.SUSPENDED)) {
mLegacyTypeTracker.update(networkAgent);
}
}
- private void updateNetworkScore(NetworkAgentInfo nai, int score) {
- if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + score);
- if (score < 0) {
- loge("updateNetworkScore for " + nai.name() + " got a negative score (" + score +
- "). Bumping score to min of 0");
- score = 0;
- }
-
- final int oldScore = nai.getCurrentScore();
- nai.setCurrentScore(score);
-
- rematchAllNetworksAndRequests(nai, oldScore);
-
+ private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) {
+ if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score);
+ nai.setScore(score);
+ rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
}
@@ -6647,6 +7271,12 @@
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
}
+ // Notify the requests on this NAI that the network is now lingered.
+ private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
+ final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+ }
+
/**
* Notify of the blocked state apps with a registered callback matching a given NAI.
*
@@ -6763,14 +7393,12 @@
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
if (VDBG || DDBG) {
String notification = ConnectivityManager.getCallbackName(notifyType);
- log("notifyType " + notification + " for " + networkAgent.name());
+ log("notifyType " + notification + " for " + networkAgent.toShortString());
}
for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
NetworkRequest nr = networkAgent.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
- // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
- // a network that no longer satisfies the listen?
if (nri.mPendingIntent == null) {
callCallbackForRequest(nri, networkAgent, notifyType, arg1);
} else {
@@ -6803,8 +7431,8 @@
}
/**
- * Notify NetworkStatsService and NetworkStatsFactory that the set of active ifaces has changed,
- * or that one of the active iface's trackedproperties has changed.
+ * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
+ * active iface's tracked properties has changed.
*/
private void notifyIfacesChangedForNetworkStats() {
ensureRunningOnConnectivityServiceThread();
@@ -6814,16 +7442,12 @@
activeIface = activeLinkProperties.getInterfaceName();
}
- // CAUTION: Ordering matters between updateVpnInfos() and forceUpdateIfaces(), which
- // triggers a new poll. Trigger the poll first to ensure a snapshot is taken before
- // switching to the new state. This ensures that traffic does not get mis-attributed to
- // incorrect apps (including VPN app).
+ final VpnInfo[] vpnInfos = getAllVpnInfo();
try {
mStatsService.forceUpdateIfaces(
- getDefaultNetworks(), getAllNetworkState(), activeIface);
+ getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos);
} catch (Exception ignored) {
}
- NetworkStatsFactory.updateVpnInfos(getAllVpnInfo());
}
@Override
@@ -6864,7 +7488,7 @@
@Override
public String getCaptivePortalServerUrl() {
- enforceConnectivityInternalPermission();
+ enforceNetworkStackOrSettingsPermission();
String settingUrl = mContext.getResources().getString(
R.string.config_networkCaptivePortalServerUrl);
@@ -6917,7 +7541,7 @@
@Override
public void factoryReset() {
- enforceConnectivityInternalPermission();
+ enforceSettingsPermission();
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
return;
@@ -6933,21 +7557,13 @@
// Turn airplane mode off
setAirplaneMode(false);
- if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
- // Untether
- String pkgName = mContext.getOpPackageName();
- for (String tether : getTetheredIfaces()) {
- untether(tether, pkgName);
- }
- }
-
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
// Remove always-on package
synchronized (mVpns) {
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
setAlwaysOnVpnPackage(userId, null, false, null);
- setVpnPackageAuthorization(alwaysOnPackage, userId, false);
+ setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
}
// Turn Always-on VPN off
@@ -6970,7 +7586,8 @@
} else {
// Prevent this app (packagename = vpnConfig.user) from initiating
// VPN connections in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ setVpnPackageAuthorization(
+ vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
}
@@ -6999,27 +7616,6 @@
return nwm.getWatchlistConfigHash();
}
- @VisibleForTesting
- MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
- return new MultinetworkPolicyTracker(c, h, r);
- }
-
- @VisibleForTesting
- public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
- return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
- }
-
- @VisibleForTesting
- public boolean hasService(String name) {
- return ServiceManager.checkService(name) != null;
- }
-
- @VisibleForTesting
- protected IpConnectivityMetrics.Logger metricsLogger() {
- return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
- "no IpConnectivityMetrics service");
- }
-
private void logNetworkEvent(NetworkAgentInfo nai, int evtype) {
int[] transports = nai.networkCapabilities.getTransportTypes();
mMetricsLog.log(nai.network.netId, transports, new NetworkEvent(evtype));
@@ -7034,9 +7630,9 @@
}
@Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
+ public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ FileDescriptor err, @NonNull String[] args, ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
(new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -7092,7 +7688,11 @@
@GuardedBy("mVpns")
private Vpn getVpnIfOwner() {
- final int uid = Binder.getCallingUid();
+ return getVpnIfOwner(Binder.getCallingUid());
+ }
+
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
final Vpn vpn = mVpns.get(user);
@@ -7130,6 +7730,13 @@
*/
public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+
+ // Only VpnService based VPNs should be able to get this information.
+ if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) {
+ throw new SecurityException(
+ "getConnectionOwnerUid() not allowed for non-VpnService VPNs");
+ }
+
if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
@@ -7178,4 +7785,460 @@
return mTNS;
}
}
+
+ /**
+ * Handler used for managing all Connectivity Diagnostics related functions.
+ *
+ * @see android.net.ConnectivityDiagnosticsManager
+ *
+ * TODO(b/147816404): Explore moving ConnectivityDiagnosticsHandler to a separate file
+ */
+ @VisibleForTesting
+ class ConnectivityDiagnosticsHandler extends Handler {
+ private final String mTag = ConnectivityDiagnosticsHandler.class.getSimpleName();
+
+ /**
+ * Used to handle ConnectivityDiagnosticsCallback registration events from {@link
+ * android.net.ConnectivityDiagnosticsManager}.
+ * obj = ConnectivityDiagnosticsCallbackInfo with IConnectivityDiagnosticsCallback and
+ * NetworkRequestInfo to be registered
+ */
+ private static final int EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 1;
+
+ /**
+ * Used to handle ConnectivityDiagnosticsCallback unregister events from {@link
+ * android.net.ConnectivityDiagnosticsManager}.
+ * obj = the IConnectivityDiagnosticsCallback to be unregistered
+ * arg1 = the uid of the caller
+ */
+ private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
+
+ /**
+ * Event for {@link NetworkStateTrackerHandler} to trigger ConnectivityReport callbacks
+ * after processing {@link #EVENT_NETWORK_TESTED} events.
+ * obj = {@link ConnectivityReportEvent} representing ConnectivityReport info reported from
+ * NetworkMonitor.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ *
+ * <p>See {@link ConnectivityService#EVENT_NETWORK_TESTED}.
+ */
+ private static final int EVENT_NETWORK_TESTED = ConnectivityService.EVENT_NETWORK_TESTED;
+
+ /**
+ * Event for NetworkMonitor to inform ConnectivityService that a potential data stall has
+ * been detected on the network.
+ * obj = Long the timestamp (in millis) for when the suspected data stall was detected.
+ * arg1 = {@link DataStallReport#DetectionMethod} indicating the detection method.
+ * arg2 = NetID.
+ * data = PersistableBundle of extras passed from NetworkMonitor.
+ */
+ private static final int EVENT_DATA_STALL_SUSPECTED = 4;
+
+ /**
+ * Event for ConnectivityDiagnosticsHandler to handle network connectivity being reported to
+ * the platform. This event will invoke {@link
+ * IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
+ * callbacks.
+ * obj = Network that was reported on
+ * arg1 = boolint for the quality reported
+ */
+ private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
+
+ private ConnectivityDiagnosticsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+ handleRegisterConnectivityDiagnosticsCallback(
+ (ConnectivityDiagnosticsCallbackInfo) msg.obj);
+ break;
+ }
+ case EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+ handleUnregisterConnectivityDiagnosticsCallback(
+ (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
+ break;
+ }
+ case EVENT_NETWORK_TESTED: {
+ final ConnectivityReportEvent reportEvent =
+ (ConnectivityReportEvent) msg.obj;
+
+ // This is safe because {@link
+ // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
+ // PersistableBundle and converts it to the Bundle in the incoming Message. If
+ // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
+ // not be set. This is also safe, as msg.getData() will return an empty Bundle.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleNetworkTestedWithExtras(reportEvent, extras);
+ break;
+ }
+ case EVENT_DATA_STALL_SUSPECTED: {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+ if (nai == null) break;
+
+ // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
+ // receives a PersistableBundle and converts it to the Bundle in the incoming
+ // Message.
+ final PersistableBundle extras = new PersistableBundle(msg.getData());
+ handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+ break;
+ }
+ case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
+ handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
+ break;
+ }
+ default: {
+ Log.e(mTag, "Unrecognized event in ConnectivityDiagnostics: " + msg.what);
+ }
+ }
+ }
+ }
+
+ /** Class used for cleaning up IConnectivityDiagnosticsCallback instances after their death. */
+ @VisibleForTesting
+ class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
+ @NonNull private final IConnectivityDiagnosticsCallback mCb;
+ @NonNull private final NetworkRequestInfo mRequestInfo;
+ @NonNull private final String mCallingPackageName;
+
+ @VisibleForTesting
+ ConnectivityDiagnosticsCallbackInfo(
+ @NonNull IConnectivityDiagnosticsCallback cb,
+ @NonNull NetworkRequestInfo nri,
+ @NonNull String callingPackageName) {
+ mCb = cb;
+ mRequestInfo = nri;
+ mCallingPackageName = callingPackageName;
+ }
+
+ @Override
+ public void binderDied() {
+ log("ConnectivityDiagnosticsCallback IBinder died.");
+ unregisterConnectivityDiagnosticsCallback(mCb);
+ }
+ }
+
+ /**
+ * Class used for sending information from {@link
+ * NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} to the handler for processing it.
+ */
+ private static class NetworkTestedResults {
+ private final int mNetId;
+ private final int mTestResult;
+ private final long mTimestampMillis;
+ @Nullable private final String mRedirectUrl;
+
+ private NetworkTestedResults(
+ int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
+ mNetId = netId;
+ mTestResult = testResult;
+ mTimestampMillis = timestampMillis;
+ mRedirectUrl = redirectUrl;
+ }
+ }
+
+ /**
+ * Class used for sending information from {@link NetworkStateTrackerHandler} to {@link
+ * ConnectivityDiagnosticsHandler}.
+ */
+ private static class ConnectivityReportEvent {
+ private final long mTimestampMillis;
+ @NonNull private final NetworkAgentInfo mNai;
+
+ private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+ mTimestampMillis = timestampMillis;
+ mNai = nai;
+ }
+ }
+
+ private void handleRegisterConnectivityDiagnosticsCallback(
+ @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
+ ensureRunningOnConnectivityServiceThread();
+
+ final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
+ final IBinder iCb = cb.asBinder();
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+
+ // This means that the client registered the same callback multiple times. Do
+ // not override the previous entry, and exit silently.
+ if (mConnectivityDiagnosticsCallbacks.containsKey(iCb)) {
+ if (VDBG) log("Diagnostics callback is already registered");
+
+ // Decrement the reference count for this NetworkRequestInfo. The reference count is
+ // incremented when the NetworkRequestInfo is created as part of
+ // enforceRequestCountLimit().
+ decrementNetworkRequestPerUidCount(nri);
+ return;
+ }
+
+ mConnectivityDiagnosticsCallbacks.put(iCb, cbInfo);
+
+ try {
+ iCb.linkToDeath(cbInfo, 0);
+ } catch (RemoteException e) {
+ cbInfo.binderDied();
+ return;
+ }
+
+ // Once registered, provide ConnectivityReports for matching Networks
+ final List<NetworkAgentInfo> matchingNetworks = new ArrayList<>();
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+ if (nai.satisfies(nri.request)) {
+ matchingNetworks.add(nai);
+ }
+ }
+ }
+ for (final NetworkAgentInfo nai : matchingNetworks) {
+ final ConnectivityReport report = nai.getConnectivityReport();
+ if (report == null) {
+ continue;
+ }
+ if (!checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ continue;
+ }
+
+ try {
+ cb.onConnectivityReportAvailable(report);
+ } catch (RemoteException e) {
+ // Exception while sending the ConnectivityReport. Move on to the next network.
+ }
+ }
+ }
+
+ private void handleUnregisterConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback cb, int uid) {
+ ensureRunningOnConnectivityServiceThread();
+ final IBinder iCb = cb.asBinder();
+
+ final ConnectivityDiagnosticsCallbackInfo cbInfo =
+ mConnectivityDiagnosticsCallbacks.remove(iCb);
+ if (cbInfo == null) {
+ if (VDBG) log("Removing diagnostics callback that is not currently registered");
+ return;
+ }
+
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+
+ if (uid != nri.mUid) {
+ if (VDBG) loge("Different uid than registrant attempting to unregister cb");
+ return;
+ }
+
+ // Decrement the reference count for this NetworkRequestInfo. The reference count is
+ // incremented when the NetworkRequestInfo is created as part of
+ // enforceRequestCountLimit().
+ decrementNetworkRequestPerUidCount(nri);
+
+ iCb.unlinkToDeath(cbInfo, 0);
+ }
+
+ private void handleNetworkTestedWithExtras(
+ @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
+ final NetworkAgentInfo nai = reportEvent.mNai;
+ final NetworkCapabilities networkCapabilities =
+ getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
+ final ConnectivityReport report =
+ new ConnectivityReport(
+ reportEvent.mNai.network,
+ reportEvent.mTimestampMillis,
+ nai.linkProperties,
+ networkCapabilities,
+ extras);
+ nai.setConnectivityReport(report);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onConnectivityReportAvailable(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onConnectivityReport", ex);
+ }
+ }
+ }
+
+ private void handleDataStallSuspected(
+ @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
+ @NonNull PersistableBundle extras) {
+ final NetworkCapabilities networkCapabilities =
+ getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
+ final DataStallReport report =
+ new DataStallReport(
+ nai.network,
+ timestampMillis,
+ detectionMethod,
+ nai.linkProperties,
+ networkCapabilities,
+ extras);
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onDataStallSuspected(report);
+ } catch (RemoteException ex) {
+ loge("Error invoking onDataStallSuspected", ex);
+ }
+ }
+ }
+
+ private void handleNetworkConnectivityReported(
+ @NonNull NetworkAgentInfo nai, boolean connectivity) {
+ final List<IConnectivityDiagnosticsCallback> results =
+ getMatchingPermissionedCallbacks(nai);
+ for (final IConnectivityDiagnosticsCallback cb : results) {
+ try {
+ cb.onNetworkConnectivityReported(nai.network, connectivity);
+ } catch (RemoteException ex) {
+ loge("Error invoking onNetworkConnectivityReported", ex);
+ }
+ }
+ }
+
+ private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
+ final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+ sanitized.setUids(null);
+ sanitized.setAdministratorUids(new int[0]);
+ sanitized.setOwnerUid(Process.INVALID_UID);
+ return sanitized;
+ }
+
+ private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
+ @NonNull NetworkAgentInfo nai) {
+ final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
+ for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
+ mConnectivityDiagnosticsCallbacks.entrySet()) {
+ final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+ if (nai.satisfies(nri.request)) {
+ if (checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ results.add(entry.getValue().mCb);
+ }
+ }
+ }
+ return results;
+ }
+
+ @VisibleForTesting
+ boolean checkConnectivityDiagnosticsPermissions(
+ int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
+ if (checkNetworkStackPermission(callbackPid, callbackUid)) {
+ return true;
+ }
+
+ // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+ // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+ // call in a try-catch.
+ try {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ return false;
+ }
+ } catch (SecurityException e) {
+ return false;
+ }
+
+ final Network[] underlyingNetworks;
+ synchronized (mVpns) {
+ final Vpn vpn = getVpnIfOwner(callbackUid);
+ underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks();
+ }
+ if (underlyingNetworks != null) {
+ if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true;
+ }
+
+ // Administrator UIDs also contains the Owner UID
+ final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
+ return ArrayUtils.contains(administratorUids, callbackUid);
+ }
+
+ @Override
+ public void registerConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback callback,
+ @NonNull NetworkRequest request,
+ @NonNull String callingPackageName) {
+ if (request.legacyType != TYPE_NONE) {
+ throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ + " Please use NetworkCapabilities instead.");
+ }
+ final int callingUid = Binder.getCallingUid();
+ mAppOpsManager.checkPackage(callingUid, callingPackageName);
+
+ // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
+ // and administrator uids to be safe.
+ final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
+
+ final NetworkRequest requestWithId =
+ new NetworkRequest(
+ nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN);
+
+ // NetworkRequestInfos created here count towards MAX_NETWORK_REQUESTS_PER_UID limit.
+ //
+ // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in
+ // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the
+ // callback's binder death.
+ final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
+ final ConnectivityDiagnosticsCallbackInfo cbInfo =
+ new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);
+
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler
+ .EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+ cbInfo));
+ }
+
+ @Override
+ public void unregisterConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback callback) {
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler
+ .EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+ Binder.getCallingUid(),
+ 0,
+ callback));
+ }
+
+ @Override
+ public void simulateDataStall(int detectionMethod, long timestampMillis,
+ @NonNull Network network, @NonNull PersistableBundle extras) {
+ enforceAnyPermissionOf(android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ android.Manifest.permission.NETWORK_STACK);
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+ if (!nc.hasTransport(TRANSPORT_TEST)) {
+ throw new SecurityException("Data Stall simluation is only possible for test networks");
+ }
+
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null || nai.creatorUid != Binder.getCallingUid()) {
+ throw new SecurityException("Data Stall simulation is only possible for network "
+ + "creators");
+ }
+
+ // Instead of passing the data stall directly to the ConnectivityDiagnostics handler, treat
+ // this as a Data Stall received directly from NetworkMonitor. This requires wrapping the
+ // Data Stall information as a DataStallReportParcelable and passing to
+ // #notifyDataStallSuspected. This ensures that unknown Data Stall detection methods are
+ // still passed to ConnectivityDiagnostics (with new detection methods masked).
+ final DataStallReportParcelable p = new DataStallReportParcelable();
+ p.timestampMillis = timestampMillis;
+ p.detectionMethod = detectionMethod;
+
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+ p.dnsConsecutiveTimeouts = extras.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS);
+ }
+ if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+ p.tcpPacketFailRate = extras.getInt(KEY_TCP_PACKET_FAIL_RATE);
+ p.tcpMetricsCollectionPeriodMillis = extras.getInt(
+ KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS);
+ }
+
+ notifyDataStallSuspected(p, network.netId);
+ }
}
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
new file mode 100644
index 0000000..097fb3a
--- /dev/null
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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 com.android.server;
+
+import android.annotation.NonNull;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Class used to reserve and release net IDs.
+ *
+ * <p>Instances of this class are thread-safe.
+ */
+public class NetIdManager {
+ // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
+ public static final int MIN_NET_ID = 100; // some reserved marks
+ // Top IDs reserved by IpSecService
+ public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+
+ @GuardedBy("mNetIdInUse")
+ private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
+
+ @GuardedBy("mNetIdInUse")
+ private int mLastNetId = MIN_NET_ID - 1;
+
+ private final int mMaxNetId;
+
+ public NetIdManager() {
+ this(MAX_NET_ID);
+ }
+
+ @VisibleForTesting
+ NetIdManager(int maxNetId) {
+ mMaxNetId = maxNetId;
+ }
+
+ /**
+ * Get the first netId that follows the provided lastId and is available.
+ */
+ private int getNextAvailableNetIdLocked(
+ int lastId, @NonNull SparseBooleanArray netIdInUse) {
+ int netId = lastId;
+ for (int i = MIN_NET_ID; i <= mMaxNetId; i++) {
+ netId = netId < mMaxNetId ? netId + 1 : MIN_NET_ID;
+ if (!netIdInUse.get(netId)) {
+ return netId;
+ }
+ }
+ throw new IllegalStateException("No free netIds");
+ }
+
+ /**
+ * Reserve a new ID for a network.
+ */
+ public int reserveNetId() {
+ synchronized (mNetIdInUse) {
+ mLastNetId = getNextAvailableNetIdLocked(mLastNetId, mNetIdInUse);
+ // Make sure NetID unused. http://b/16815182
+ mNetIdInUse.put(mLastNetId, true);
+ return mLastNetId;
+ }
+ }
+
+ /**
+ * Clear a previously reserved ID for a network.
+ */
+ public void releaseNetId(int id) {
+ synchronized (mNetIdInUse) {
+ mNetIdInUse.delete(id);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 40bf7bc..d6bd5a1 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -16,9 +16,11 @@
package com.android.server;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
+import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
@@ -53,14 +55,14 @@
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/** @hide */
class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
@NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
- @NonNull private static final String TEST_TUN_PREFIX = "testtun";
- @NonNull private static final String TEST_TAP_PREFIX = "testtap";
@NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
@NonNull private final Context mContext;
@@ -80,9 +82,9 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
- mContext = checkNotNull(context, "missing Context");
- mNMS = checkNotNull(netManager, "missing INetworkManagementService");
- mNetd = checkNotNull(NetdService.getInstance(), "could not get netd instance");
+ mContext = Objects.requireNonNull(context, "missing Context");
+ mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+ mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
}
/**
@@ -94,7 +96,7 @@
private TestNetworkInterface createInterface(boolean isTun, LinkAddress[] linkAddrs) {
enforceTestNetworkPermissions(mContext);
- checkNotNull(linkAddrs, "missing linkAddrs");
+ Objects.requireNonNull(linkAddrs, "missing linkAddrs");
String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
@@ -217,7 +219,7 @@
// Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up
// resources, even for binder death or unwanted calls.
synchronized (mTestNetworkTracker) {
- mTestNetworkTracker.remove(netId);
+ mTestNetworkTracker.remove(getNetwork().netId);
}
}
}
@@ -226,11 +228,14 @@
@NonNull Looper looper,
@NonNull Context context,
@NonNull String iface,
+ @Nullable LinkProperties lp,
+ boolean isMetered,
int callingUid,
+ @NonNull int[] administratorUids,
@NonNull IBinder binder)
throws RemoteException, SocketException {
- checkNotNull(looper, "missing Looper");
- checkNotNull(context, "missing Context");
+ Objects.requireNonNull(looper, "missing Looper");
+ Objects.requireNonNull(context, "missing Context");
// iface and binder validity checked by caller
// Build network info with special testing type
@@ -245,15 +250,26 @@
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+ nc.setAdministratorUids(administratorUids);
+ if (!isMetered) {
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
// Build LinkProperties
- LinkProperties lp = new LinkProperties();
+ if (lp == null) {
+ lp = new LinkProperties();
+ } else {
+ lp = new LinkProperties(lp);
+ // Use LinkAddress(es) from the interface itself to minimize how much the caller
+ // is trusted.
+ lp.setLinkAddresses(new ArrayList<>());
+ }
lp.setInterfaceName(iface);
// Find the currently assigned addresses, and add them to LinkProperties
boolean allowIPv4 = false, allowIPv6 = false;
NetworkInterface netIntf = NetworkInterface.getByName(iface);
- checkNotNull(netIntf, "No such network interface found: " + netIntf);
+ Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf);
for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
lp.addLinkAddress(
@@ -284,11 +300,16 @@
* <p>This method provides a Network that is useful only for testing.
*/
@Override
- public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
+ public void setupTestNetwork(
+ @NonNull String iface,
+ @Nullable LinkProperties lp,
+ boolean isMetered,
+ @NonNull int[] administratorUids,
+ @NonNull IBinder binder) {
enforceTestNetworkPermissions(mContext);
- checkNotNull(iface, "missing Iface");
- checkNotNull(binder, "missing IBinder");
+ Objects.requireNonNull(iface, "missing Iface");
+ Objects.requireNonNull(binder, "missing IBinder");
if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX)
|| iface.startsWith(TEST_TUN_PREFIX))) {
@@ -296,36 +317,34 @@
"Cannot create network for non ipsec, non-testtun interface");
}
- // Setup needs to be done with NETWORK_STACK privileges.
- int callingUid = Binder.getCallingUid();
- Binder.withCleanCallingIdentity(
- () -> {
- try {
- mNMS.setInterfaceUp(iface);
+ try {
+ // This requires NETWORK_STACK privileges.
+ Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
- // Synchronize all accesses to mTestNetworkTracker to prevent the case
- // where:
- // 1. TestNetworkAgent successfully binds to death of binder
- // 2. Before it is added to the mTestNetworkTracker, binder dies,
- // binderDied() is called (on a different thread)
- // 3. This thread is pre-empted, put() is called after remove()
- synchronized (mTestNetworkTracker) {
- TestNetworkAgent agent =
- registerTestNetworkAgent(
- mHandler.getLooper(),
- mContext,
- iface,
- callingUid,
- binder);
+ // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
+ // 1. TestNetworkAgent successfully binds to death of binder
+ // 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
+ // (on a different thread)
+ // 3. This thread is pre-empted, put() is called after remove()
+ synchronized (mTestNetworkTracker) {
+ TestNetworkAgent agent =
+ registerTestNetworkAgent(
+ mHandler.getLooper(),
+ mContext,
+ iface,
+ lp,
+ isMetered,
+ Binder.getCallingUid(),
+ administratorUids,
+ binder);
- mTestNetworkTracker.put(agent.netId, agent);
- }
- } catch (SocketException e) {
- throw new UncheckedIOException(e);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- });
+ mTestNetworkTracker.put(agent.getNetwork().netId, agent);
+ }
+ } catch (SocketException e) {
+ throw new UncheckedIOException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Teardown a test network */
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 2321afb..cf6a7f6 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -27,6 +27,7 @@
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +35,7 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
+import android.net.ResolverOptionsParcel;
import android.net.ResolverParamsParcel;
import android.net.Uri;
import android.net.shared.PrivateDnsConfig;
@@ -55,6 +57,7 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@@ -62,7 +65,9 @@
* Encapsulate the management of DNS settings for networks.
*
* This class it NOT designed for concurrent access. Furthermore, all non-static
- * methods MUST be called from ConnectivityService's thread.
+ * methods MUST be called from ConnectivityService's thread. However, an exceptional
+ * case is getPrivateDnsConfig(Network) which is exclusively for
+ * ConnectivityService#dumpNetworkDiagnostics() on a random binder thread.
*
* [ Private DNS ]
* The code handling Private DNS is spread across several components, but this
@@ -234,25 +239,27 @@
private final ContentResolver mContentResolver;
private final IDnsResolver mDnsResolver;
private final MockableSystemProperties mSystemProperties;
- // TODO: Replace these Maps with SparseArrays.
- private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+ private final ConcurrentHashMap<Integer, PrivateDnsConfig> mPrivateDnsMap;
+ // TODO: Replace the Map with SparseArrays.
private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
+ private final Map<Integer, LinkProperties> mLinkPropertiesMap;
+ private final Map<Integer, int[]> mTransportsMap;
private int mNumDnsEntries;
private int mSampleValidity;
private int mSuccessThreshold;
private int mMinSamples;
private int mMaxSamples;
- private String mPrivateDnsMode;
- private String mPrivateDnsSpecifier;
public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) {
mContext = ctx;
mContentResolver = mContext.getContentResolver();
mDnsResolver = dnsResolver;
mSystemProperties = sp;
- mPrivateDnsMap = new HashMap<>();
+ mPrivateDnsMap = new ConcurrentHashMap<>();
mPrivateDnsValidationMap = new HashMap<>();
+ mLinkPropertiesMap = new HashMap<>();
+ mTransportsMap = new HashMap<>();
// TODO: Create and register ContentObservers to track every setting
// used herein, posting messages to respond to changes.
@@ -265,6 +272,14 @@
public void removeNetwork(Network network) {
mPrivateDnsMap.remove(network.netId);
mPrivateDnsValidationMap.remove(network.netId);
+ mTransportsMap.remove(network.netId);
+ mLinkPropertiesMap.remove(network.netId);
+ }
+
+ // This is exclusively called by ConnectivityService#dumpNetworkDiagnostics() which
+ // is not on the ConnectivityService handler thread.
+ public PrivateDnsConfig getPrivateDnsConfig(@NonNull Network network) {
+ return mPrivateDnsMap.getOrDefault(network.netId, PRIVATE_DNS_OFF);
}
public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -304,9 +319,35 @@
statuses.updateStatus(update);
}
- public void setDnsConfigurationForNetwork(
- int netId, LinkProperties lp, boolean isDefaultNetwork) {
+ /**
+ * When creating a new network or transport types are changed in a specific network,
+ * transport types are always saved to a hashMap before update dns config.
+ * When destroying network, the specific network will be removed from the hashMap.
+ * The hashMap is always accessed on the same thread.
+ */
+ public void updateTransportsForNetwork(int netId, @NonNull int[] transportTypes) {
+ mTransportsMap.put(netId, transportTypes);
+ sendDnsConfigurationForNetwork(netId);
+ }
+ /**
+ * When {@link LinkProperties} are changed in a specific network, they are
+ * always saved to a hashMap before update dns config.
+ * When destroying network, the specific network will be removed from the hashMap.
+ * The hashMap is always accessed on the same thread.
+ */
+ public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) {
+ mLinkPropertiesMap.put(netId, lp);
+ sendDnsConfigurationForNetwork(netId);
+ }
+
+ /**
+ * Send dns configuration parameters to resolver for a given network.
+ */
+ public void sendDnsConfigurationForNetwork(int netId) {
+ final LinkProperties lp = mLinkPropertiesMap.get(netId);
+ final int[] transportTypes = mTransportsMap.get(netId);
+ if (lp == null || transportTypes == null) return;
updateParametersSettings();
final ResolverParamsParcel paramsParcel = new ResolverParamsParcel();
@@ -319,15 +360,16 @@
// networks like IMS.
final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
PRIVATE_DNS_OFF);
-
final boolean useTls = privateDnsCfg.useTls;
final boolean strictMode = privateDnsCfg.inStrictMode();
+
paramsParcel.netId = netId;
paramsParcel.sampleValiditySeconds = mSampleValidity;
paramsParcel.successThreshold = mSuccessThreshold;
paramsParcel.minSamples = mMinSamples;
paramsParcel.maxSamples = mMaxSamples;
- paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers());
+ paramsParcel.servers =
+ NetworkUtils.makeStrings(lp.getDnsServers());
paramsParcel.domains = getDomainStrings(lp.getDomains());
paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
paramsParcel.tlsServers =
@@ -337,7 +379,8 @@
.collect(Collectors.toList()))
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
- paramsParcel.tlsFingerprints = new String[0];
+ paramsParcel.resolverOptions = new ResolverOptionsParcel();
+ paramsParcel.transportTypes = transportTypes;
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.
if (useTls) {
@@ -350,7 +393,7 @@
mPrivateDnsValidationMap.remove(netId);
}
- Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
paramsParcel.successThreshold, paramsParcel.minSamples,
@@ -364,13 +407,6 @@
Slog.e(TAG, "Error setting DNS configuration: " + e);
return;
}
-
- // TODO: netd should listen on [::1]:53 and proxy queries to the current
- // default network, and we should just set net.dns1 to ::1, not least
- // because applications attempting to use net.dns resolvers will bypass
- // the privacy protections of things like DNS-over-TLS.
- if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
- flushVmDnsCache();
}
public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
@@ -385,7 +421,10 @@
mNumDnsEntries = last;
}
- private void flushVmDnsCache() {
+ /**
+ * Flush DNS caches and events work before boot has completed.
+ */
+ public void flushVmDnsCache() {
/*
* Tell the VMs to toss their DNS caches
*/
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index cb64245..7c8fb5a 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -39,11 +39,11 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.ISocketKeepaliveCallback;
+import android.net.InvalidPacketException;
import android.net.KeepalivePacketData;
import android.net.NattKeepalivePacketData;
import android.net.NetworkAgent;
import android.net.NetworkUtils;
-import android.net.SocketKeepalive.InvalidPacketException;
import android.net.SocketKeepalive.InvalidSocketException;
import android.net.TcpKeepalivePacketData;
import android.net.util.IpUtils;
@@ -220,9 +220,9 @@
+ " network=" + mNai.network
+ " startedState=" + startedStateString(mStartedState)
+ " "
- + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)
+ + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort())
+ "->"
- + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+ + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort())
+ " interval=" + mInterval
+ " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
+ " packetData=" + HexDump.toHexString(mPacket.getPacket())
@@ -250,7 +250,7 @@
private int checkSourceAddress() {
// Check that we have the source address.
for (InetAddress address : mNai.linkProperties.getAddresses()) {
- if (address.equals(mPacket.srcAddress)) {
+ if (address.equals(mPacket.getSrcAddress())) {
return SUCCESS;
}
}
@@ -325,7 +325,7 @@
mSlot = slot;
int error = isValid();
if (error == SUCCESS) {
- Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+ Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
switch (mType) {
case TYPE_NATT:
mNai.asyncChannel.sendMessage(
@@ -365,7 +365,8 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+ Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
+ + ": " + reason);
switch (mStartedState) {
case NOT_STARTED:
// Remove the reference of the keepalive that meet error before starting,
@@ -473,12 +474,10 @@
cleanupStoppedKeepalive(nai, ki.mSlot);
}
}
- // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
- // freed.
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
- String networkName = (nai == null) ? "(null)" : nai.name();
+ final String networkName = NetworkAgentInfo.toShortString(nai);
HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
@@ -495,7 +494,7 @@
}
private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
- String networkName = (nai == null) ? "(null)" : nai.name();
+ final String networkName = NetworkAgentInfo.toShortString(nai);
HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
@@ -542,7 +541,7 @@
} catch(NullPointerException e) {}
if (ki == null) {
Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
- + " for unknown keepalive " + slot + " on " + nai.name());
+ + " for unknown keepalive " + slot + " on " + nai.toShortString());
return;
}
@@ -564,7 +563,7 @@
if (KeepaliveInfo.STARTING == ki.mStartedState) {
if (SUCCESS == reason) {
// Keepalive successfully started.
- Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+ Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
ki.mStartedState = KeepaliveInfo.STARTED;
try {
ki.mCallback.onStarted(slot);
@@ -572,14 +571,14 @@
Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
}
} else {
- Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
+ Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
+ ": " + reason);
// The message indicated some error trying to start: do call handleStopKeepalive.
handleStopKeepalive(nai, slot, reason);
}
} else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
// The message indicated result of stopping : clean up keepalive slots.
- Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+ Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
+ " stopped: " + reason);
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
cleanupStoppedKeepalive(nai, slot);
@@ -620,7 +619,7 @@
packet = NattKeepalivePacketData.nattKeepalivePacket(
srcAddress, srcPort, dstAddress, NATT_PORT);
} catch (InvalidPacketException e) {
- notifyErrorCallback(cb, e.error);
+ notifyErrorCallback(cb, e.getError());
return;
}
KeepaliveInfo ki = null;
@@ -659,9 +658,12 @@
final TcpKeepalivePacketData packet;
try {
packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
- } catch (InvalidPacketException | InvalidSocketException e) {
+ } catch (InvalidSocketException e) {
notifyErrorCallback(cb, e.error);
return;
+ } catch (InvalidPacketException e) {
+ notifyErrorCallback(cb, e.getError());
+ return;
}
KeepaliveInfo ki = null;
try {
@@ -732,7 +734,7 @@
pw.println("Socket keepalives:");
pw.increaseIndent();
for (NetworkAgentInfo nai : mKeepalives.keySet()) {
- pw.println(nai.name());
+ pw.println(nai.toShortString());
pw.increaseIndent();
for (int slot : mKeepalives.get(nai).keySet()) {
KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 929dfc4..04c000f 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -16,6 +16,10 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityManager.NETID_UNSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -27,18 +31,16 @@
import android.text.format.DateUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.SparseBooleanArray;
-import java.util.Arrays;
-import java.util.HashMap;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
-import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import static android.net.ConnectivityManager.NETID_UNSET;
+import java.util.Arrays;
+import java.util.HashMap;
/**
* Class that monitors default network linger events and possibly notifies the user of network
@@ -198,21 +200,33 @@
}
if (DBG) {
- Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
- " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+ Log.d(TAG, "Notifying switch from=" + fromNai.toShortString()
+ + " to=" + toNai.toShortString()
+ + " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
}
mNotifications.put(fromNai.network.netId, toNai.network.netId);
mEverNotified.put(fromNai.network.netId, true);
}
+ /**
+ * Put up or dismiss a notification or toast for of a change in the default network if needed.
+ *
+ * Putting up a notification when switching from no network to some network is not supported
+ * and as such this method can't be called with a null |fromNai|. It can be called with a
+ * null |toNai| if there isn't a default network any more.
+ *
+ * @param fromNai switching from this NAI
+ * @param toNai switching to this NAI
+ */
// The default network changed from fromNai to toNai due to a change in score.
- public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
+ @Nullable final NetworkAgentInfo toNai) {
if (VDBG) {
- Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
- " everValidated=" + fromNai.everValidated +
- " lastValidated=" + fromNai.lastValidated +
- " to=" + toNai.name());
+ Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.toShortString()
+ + " everValidated=" + fromNai.everValidated
+ + " lastValidated=" + fromNai.lastValidated
+ + " to=" + toNai.toShortString());
}
// If we are currently notifying the user because the device switched to fromNai, now that
@@ -221,6 +235,10 @@
// Internet access).
maybeStopNotifying(fromNai);
+ // If the network was simply lost (either because it disconnected or because it stopped
+ // being the default with no replacement), then don't show a notification.
+ if (null == toNai) return;
+
// If this network never validated, don't notify. Otherwise, we could do things like:
//
// 1. Unvalidated wifi connects.
@@ -253,7 +271,8 @@
// TODO: should we do this?
if (everNotified(fromNai)) {
if (VDBG) {
- Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+ Log.d(TAG, "Not notifying handover from " + fromNai.toShortString()
+ + ", already notified");
}
return;
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 66bd27c..3091a71 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
import android.net.ConnectivityManager;
import android.net.IDnsResolver;
import android.net.INetd;
@@ -80,12 +81,23 @@
RUNNING, // start() called, and the stacked iface is known to be up.
}
- private IpPrefix mNat64Prefix;
+ /**
+ * NAT64 prefix currently in use. Only valid in STARTING or RUNNING states.
+ * Used, among other things, to avoid updates when switching from a prefix learned from one
+ * source (e.g., RA) to the same prefix learned from another source (e.g., RA).
+ */
+ private IpPrefix mNat64PrefixInUse;
+ /** NAT64 prefix (if any) discovered from DNS via RFC 7050. */
+ private IpPrefix mNat64PrefixFromDns;
+ /** NAT64 prefix (if any) learned from the network via RA. */
+ private IpPrefix mNat64PrefixFromRa;
private String mBaseIface;
private String mIface;
private Inet6Address mIPv6Address;
private State mState = State.IDLE;
+ private boolean mPrefixDiscoveryRunning;
+
public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
INetworkManagementService nmService) {
mDnsResolver = dnsResolver;
@@ -99,7 +111,7 @@
* currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
* enable NAT64 prefix discovery.
*
- * @param network the NetworkAgentInfo corresponding to the network.
+ * @param nai the NetworkAgentInfo corresponding to the network.
* @return true if the network requires clat, false otherwise.
*/
@VisibleForTesting
@@ -115,7 +127,8 @@
&& !lp.hasIpv4Address();
// If the network tells us it doesn't use clat, respect that.
- final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+ final boolean skip464xlat = (nai.netAgentConfig() != null)
+ && nai.netAgentConfig().skip464xlat;
return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
}
@@ -134,15 +147,6 @@
}
/**
- * @return true if we have started prefix discovery and not yet stopped it (regardless of
- * whether it is still running or has succeeded).
- * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING.
- */
- public boolean isPrefixDiscoveryStarted() {
- return mState == State.DISCOVERING || isStarted();
- }
-
- /**
* @return true if clatd has been started and has not yet stopped.
* A true result corresponds to internal states STARTING and RUNNING.
*/
@@ -172,13 +176,14 @@
try {
mNMService.registerObserver(this);
} catch (RemoteException e) {
- Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
+ Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
return;
}
+ mNat64PrefixInUse = selectNat64Prefix();
String addrStr = null;
try {
- addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString());
+ addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
}
@@ -190,6 +195,12 @@
} catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
Slog.e(TAG, "Invalid IPv6 address " + addrStr);
}
+ if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
+ stopPrefixDiscovery();
+ }
+ if (!mPrefixDiscoveryRunning) {
+ setPrefix64(mNat64PrefixInUse);
+ }
}
/**
@@ -209,10 +220,18 @@
} catch (RemoteException | IllegalStateException e) {
Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
}
+ mNat64PrefixInUse = null;
mIface = null;
mBaseIface = null;
- mState = State.IDLE;
- if (requiresClat(mNetwork)) {
+
+ if (!mPrefixDiscoveryRunning) {
+ setPrefix64(null);
+ }
+
+ if (isPrefixDiscoveryNeeded()) {
+ if (!mPrefixDiscoveryRunning) {
+ startPrefixDiscovery();
+ }
mState = State.DISCOVERING;
} else {
stopPrefixDiscovery();
@@ -274,10 +293,10 @@
private void startPrefixDiscovery() {
try {
mDnsResolver.startPrefix64Discovery(getNetId());
- mState = State.DISCOVERING;
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
}
+ mPrefixDiscoveryRunning = true;
}
private void stopPrefixDiscovery() {
@@ -286,38 +305,100 @@
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
}
+ mPrefixDiscoveryRunning = false;
+ }
+
+ private boolean isPrefixDiscoveryNeeded() {
+ // If there is no NAT64 prefix in the RA, prefix discovery is always needed. It cannot be
+ // stopped after it succeeds, because stopping it will cause netd to report that the prefix
+ // has been removed, and that will cause us to stop clatd.
+ return requiresClat(mNetwork) && mNat64PrefixFromRa == null;
+ }
+
+ private void setPrefix64(IpPrefix prefix) {
+ final String prefixString = (prefix != null) ? prefix.toString() : "";
+ try {
+ mDnsResolver.setPrefix64(getNetId(), prefixString);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ + prefix + ": " + e);
+ }
+ }
+
+ private void maybeHandleNat64PrefixChange() {
+ final IpPrefix newPrefix = selectNat64Prefix();
+ if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
+ Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ + newPrefix);
+ stop();
+ // It's safe to call update here, even though this method is called from update, because
+ // stop() is guaranteed to have moved out of STARTING and RUNNING, which are the only
+ // states in which this method can be called.
+ update();
+ }
}
/**
* Starts/stops NAT64 prefix discovery and clatd as necessary.
*/
public void update() {
- // TODO: turn this class into a proper StateMachine. // http://b/126113090
- if (requiresClat(mNetwork)) {
- if (!isPrefixDiscoveryStarted()) {
- startPrefixDiscovery();
- } else if (shouldStartClat(mNetwork)) {
- // NAT64 prefix detected. Start clatd.
- // TODO: support the NAT64 prefix changing after it's been discovered. There is no
- // need to support this at the moment because it cannot happen without changes to
- // the Dns64Configuration code in netd.
- start();
- } else {
- // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
- stop();
- }
- } else {
- // Network no longer requires clat. Stop clat and prefix discovery.
- if (isStarted()) {
- stop();
- } else if (isPrefixDiscoveryStarted()) {
- leaveStartedState();
- }
+ // TODO: turn this class into a proper StateMachine. http://b/126113090
+ switch (mState) {
+ case IDLE:
+ if (isPrefixDiscoveryNeeded()) {
+ startPrefixDiscovery(); // Enters DISCOVERING state.
+ mState = State.DISCOVERING;
+ } else if (requiresClat(mNetwork)) {
+ start(); // Enters STARTING state.
+ }
+ break;
+
+ case DISCOVERING:
+ if (shouldStartClat(mNetwork)) {
+ // NAT64 prefix detected. Start clatd.
+ start(); // Enters STARTING state.
+ return;
+ }
+ if (!requiresClat(mNetwork)) {
+ // IPv4 address added. Go back to IDLE state.
+ stopPrefixDiscovery();
+ mState = State.IDLE;
+ return;
+ }
+ break;
+
+ case STARTING:
+ case RUNNING:
+ // NAT64 prefix removed, or IPv4 address added.
+ // Stop clatd and go back into DISCOVERING or idle.
+ if (!shouldStartClat(mNetwork)) {
+ stop();
+ break;
+ }
+ // Only necessary while clat is actually started.
+ maybeHandleNat64PrefixChange();
+ break;
}
}
- public void setNat64Prefix(IpPrefix nat64Prefix) {
- mNat64Prefix = nat64Prefix;
+ /**
+ * Picks a NAT64 prefix to use. Always prefers the prefix from the RA if one is received from
+ * both RA and DNS, because the prefix in the RA has better security and updatability, and will
+ * almost always be received first anyway.
+ *
+ * Any network that supports legacy hosts will support discovering the DNS64 prefix via DNS as
+ * well. If the prefix from the RA is withdrawn, fall back to that for reliability purposes.
+ */
+ private IpPrefix selectNat64Prefix() {
+ return mNat64PrefixFromRa != null ? mNat64PrefixFromRa : mNat64PrefixFromDns;
+ }
+
+ public void setNat64PrefixFromRa(IpPrefix prefix) {
+ mNat64PrefixFromRa = prefix;
+ }
+
+ public void setNat64PrefixFromDns(IpPrefix prefix) {
+ mNat64PrefixFromDns = prefix;
}
/**
@@ -325,13 +406,15 @@
* This is necessary because the LinkProperties in mNetwork come from the transport layer, which
* has no idea that 464xlat is running on top of it.
*/
- public void fixupLinkProperties(LinkProperties oldLp, LinkProperties lp) {
- lp.setNat64Prefix(mNat64Prefix);
+ public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
+ // This must be done even if clatd is not running, because otherwise shouldStartClat would
+ // never return true.
+ lp.setNat64Prefix(selectNat64Prefix());
if (!isRunning()) {
return;
}
- if (lp == null || lp.getAllInterfaceNames().contains(mIface)) {
+ if (lp.getAllInterfaceNames().contains(mIface)) {
return;
}
@@ -434,7 +517,7 @@
@Override
public void interfaceRemoved(String iface) {
- mNetwork.handler().post(() -> { handleInterfaceRemoved(iface); });
+ mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5b04379..37b2de1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,15 +16,21 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.NetworkCapabilities.transportNamesOf;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.net.CaptivePortalData;
import android.net.IDnsResolver;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
import android.net.NetworkState;
@@ -116,7 +122,7 @@
// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
- public NetworkInfo networkInfo;
+ @NonNull public NetworkInfo networkInfo;
// This Network object should always be used if possible, so as to encourage reuse of the
// enclosed socket factory and connection pool. Avoid creating other Network objects.
// This Network object is always valid.
@@ -125,7 +131,7 @@
// This should only be modified by ConnectivityService, via setNetworkCapabilities().
// TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
- public final NetworkMisc networkMisc;
+ public final NetworkAgentConfig networkAgentConfig;
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
// This is a sticky bit; once set it is never cleared.
@@ -155,13 +161,16 @@
// Whether a captive portal was found during the last network validation attempt.
public boolean lastCaptivePortalDetected;
- // Indicates the captive portal app was opened to show a login UI to the user, but the network
- // has not validated yet.
- public volatile boolean captivePortalValidationPending;
-
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
+ // Captive portal info of the network, if any.
+ // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
+ public CaptivePortalData captivePortalData;
+
+ // The UID of the remote entity that created this Network.
+ public final int creatorUid;
+
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
@@ -226,8 +235,8 @@
// validated).
private boolean mLingering;
- // This represents the last score received from the NetworkAgent.
- private int currentScore;
+ // This represents the quality of the network with no clear scale.
+ private int mScore;
// The list of NetworkRequests being satisfied by this Network.
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
@@ -238,6 +247,10 @@
// How many of the satisfied requests are of type BACKGROUND_REQUEST.
private int mNumBackgroundNetworkRequests = 0;
+ // The last ConnectivityReport made available for this network. This value is only null before a
+ // report is generated. Once non-null, it will never be null again.
+ @Nullable private ConnectivityReport mConnectivityReport;
+
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -256,22 +269,24 @@
private final Handler mHandler;
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
- LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
- NetworkMisc misc, ConnectivityService connService, INetd netd,
- IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
+ LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+ Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
+ IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
+ int creatorUid) {
this.messenger = messenger;
asyncChannel = ac;
network = net;
networkInfo = info;
linkProperties = lp;
networkCapabilities = nc;
- currentScore = score;
+ mScore = score;
clatd = new Nat464Xlat(this, netd, dnsResolver, nms);
mConnService = connService;
mContext = context;
mHandler = handler;
- networkMisc = misc;
+ networkAgentConfig = config;
this.factorySerialNumber = factorySerialNumber;
+ this.creatorUid = creatorUid;
}
/**
@@ -287,21 +302,26 @@
*
* <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
* the exception is logged but not reported to callers.
+ *
+ * @return the old capabilities of this network.
*/
- public void setNetworkCapabilities(NetworkCapabilities nc) {
+ public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
+ @NonNull final NetworkCapabilities nc) {
+ final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc;
final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) {
nm.notifyNetworkCapabilitiesChanged(nc);
}
+ return oldNc;
}
public ConnectivityService connService() {
return mConnService;
}
- public NetworkMisc netMisc() {
- return networkMisc;
+ public NetworkAgentConfig netAgentConfig() {
+ return networkAgentConfig;
}
public Handler handler() {
@@ -363,7 +383,7 @@
// Should only happen if the requestId wraps. If that happens lots of other things will
// be broken as well.
Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
- networkRequest, existing, name()));
+ networkRequest, existing, toShortString()));
updateRequestCounts(REMOVE, existing);
}
mNetworkRequests.put(networkRequest.requestId, networkRequest);
@@ -442,15 +462,6 @@
&& !isLingering();
}
- /**
- * Returns whether this network is currently suspended. A network is suspended if it is still
- * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
- * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
- */
- public boolean isSuspended() {
- return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
- }
-
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
@@ -478,11 +489,12 @@
// selected and we're trying to see what its score could be. This ensures that we don't tear
// down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available.
- if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ if (networkAgentConfig.explicitlySelected
+ && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
- int score = currentScore;
+ int score = mScore;
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
@@ -511,15 +523,16 @@
return getCurrentScore(true);
}
- public void setCurrentScore(int newScore) {
- currentScore = newScore;
+ public void setScore(final int score) {
+ mScore = score;
}
public NetworkState getNetworkState() {
synchronized (this) {
// Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
- final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
+ final String subscriberId = (networkAgentConfig != null)
+ ? networkAgentConfig.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
@@ -535,11 +548,11 @@
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
- Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+ Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
}
final long expiryMs = now + duration;
LingerTimer timer = new LingerTimer(request, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+ if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
mLingerTimers.add(timer);
mLingerTimerForRequest.put(request.requestId, timer);
}
@@ -551,7 +564,7 @@
public boolean unlingerRequest(NetworkRequest request) {
LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+ if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
mLingerTimers.remove(timer);
mLingerTimerForRequest.remove(request.requestId);
return true;
@@ -571,7 +584,7 @@
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
// never call its callback (handleLingerComplete), even if it has already fired.
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
- // has already been dispatched, rescheduling to some time in the future it won't stop it
+ // has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
if (mLingerMessage != null) {
mLingerMessage.cancel();
@@ -579,10 +592,12 @@
}
if (newExpiry > 0) {
- mLingerMessage = mConnService.makeWakeupMessage(
+ mLingerMessage = new WakeupMessage(
mContext, mHandler,
- "NETWORK_LINGER_COMPLETE." + network.netId,
- EVENT_NETWORK_LINGER_COMPLETE, this);
+ "NETWORK_LINGER_COMPLETE." + network.netId /* cmdName */,
+ EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
+ 0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
+ this /* obj (NetworkAgentInfo) */);
mLingerMessage.schedule(newExpiry);
}
@@ -616,6 +631,30 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
+ /**
+ * Sets the most recent ConnectivityReport for this network.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ public void setConnectivityReport(@NonNull ConnectivityReport connectivityReport) {
+ mConnectivityReport = connectivityReport;
+ }
+
+ /**
+ * Returns the most recent ConnectivityReport for this network, or null if none have been
+ * reported yet.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ @Nullable
+ public ConnectivityReport getConnectivityReport() {
+ return mConnectivityReport;
+ }
+
// TODO: Print shorter members first and only print the boolean variable which value is true
// to improve readability.
public String toString() {
@@ -625,20 +664,26 @@
+ "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} "
+ "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} "
+ "created{" + created + "} lingering{" + isLingering() + "} "
- + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
- + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+ + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
+ + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
+ "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
+ "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
- + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
+ "partialConnectivity{" + partialConnectivity + "} "
- + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+ + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
+ "clat{" + clatd + "} "
+ "}";
}
- public String name() {
- return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
- networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
+ /**
+ * Show a short string representing a Network.
+ *
+ * This is often not enough for debugging purposes for anything complex, but the full form
+ * is very long and hard to read, so this is useful when there isn't a lot of ambiguity.
+ * This represents the network with something like "[100 WIFI|VPN]" or "[108 MOBILE]".
+ */
+ public String toShortString() {
+ return "[" + network.netId + " "
+ + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
}
// Enables sorting in descending order of score.
@@ -646,4 +691,12 @@
public int compareTo(NetworkAgentInfo other) {
return other.getCurrentScore() - getCurrentScore();
}
+
+ /**
+ * Null-guarding version of NetworkAgentInfo#toShortString()
+ */
+ @NonNull
+ public static String toShortString(@Nullable final NetworkAgentInfo nai) {
+ return null != nai ? nai.toShortString() : "[null]";
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a1a8e35..49c16ad 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,12 +18,15 @@
import static android.system.OsConstants.*;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TrafficStats;
+import android.net.shared.PrivateDnsConfig;
import android.net.util.NetworkConstants;
import android.os.SystemClock;
import android.system.ErrnoException;
@@ -38,6 +41,8 @@
import libcore.io.IoUtils;
import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
@@ -52,6 +57,7 @@
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -59,6 +65,12 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
/**
* NetworkDiagnostics
*
@@ -100,6 +112,7 @@
private final Network mNetwork;
private final LinkProperties mLinkProperties;
+ private final PrivateDnsConfig mPrivateDnsCfg;
private final Integer mInterfaceIndex;
private final long mTimeoutMs;
@@ -163,12 +176,15 @@
private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
new HashMap<>();
private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
+ private final Map<InetAddress, Measurement> mDnsTlsChecks = new HashMap<>();
private final String mDescription;
- public NetworkDiagnostics(Network network, LinkProperties lp, long timeoutMs) {
+ public NetworkDiagnostics(Network network, LinkProperties lp,
+ @NonNull PrivateDnsConfig privateDnsCfg, long timeoutMs) {
mNetwork = network;
mLinkProperties = lp;
+ mPrivateDnsCfg = privateDnsCfg;
mInterfaceIndex = getInterfaceIndex(mLinkProperties.getInterfaceName());
mTimeoutMs = timeoutMs;
mStartTime = now();
@@ -199,8 +215,22 @@
}
}
for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
- prepareIcmpMeasurement(nameserver);
- prepareDnsMeasurement(nameserver);
+ prepareIcmpMeasurement(nameserver);
+ prepareDnsMeasurement(nameserver);
+
+ // Unlike the DnsResolver which doesn't do certificate validation in opportunistic mode,
+ // DoT probes to the DNS servers will fail if certificate validation fails.
+ prepareDnsTlsMeasurement(null /* hostname */, nameserver);
+ }
+
+ for (InetAddress tlsNameserver : mPrivateDnsCfg.ips) {
+ // Reachability check is necessary since when resolving the strict mode hostname,
+ // NetworkMonitor always queries for both A and AAAA records, even if the network
+ // is IPv4-only or IPv6-only.
+ if (mLinkProperties.isReachable(tlsNameserver)) {
+ // If there are IPs, there must have been a name that resolved to them.
+ prepareDnsTlsMeasurement(mPrivateDnsCfg.hostname, tlsNameserver);
+ }
}
mCountDownLatch = new CountDownLatch(totalMeasurementCount());
@@ -222,6 +252,15 @@
}
}
+ private static String socketAddressToString(@NonNull SocketAddress sockAddr) {
+ // The default toString() implementation is not the prettiest.
+ InetSocketAddress inetSockAddr = (InetSocketAddress) sockAddr;
+ InetAddress localAddr = inetSockAddr.getAddress();
+ return String.format(
+ (localAddr instanceof Inet6Address ? "[%s]:%d" : "%s:%d"),
+ localAddr.getHostAddress(), inetSockAddr.getPort());
+ }
+
private void prepareIcmpMeasurement(InetAddress target) {
if (!mIcmpChecks.containsKey(target)) {
Measurement measurement = new Measurement();
@@ -252,8 +291,19 @@
}
}
+ private void prepareDnsTlsMeasurement(@Nullable String hostname, @NonNull InetAddress target) {
+ // This might overwrite an existing entry in mDnsTlsChecks, because |target| can be an IP
+ // address configured by the network as well as an IP address learned by resolving the
+ // strict mode DNS hostname. If the entry is overwritten, the overwritten measurement
+ // thread will not execute.
+ Measurement measurement = new Measurement();
+ measurement.thread = new Thread(new DnsTlsCheck(hostname, target, measurement));
+ mDnsTlsChecks.put(target, measurement);
+ }
+
private int totalMeasurementCount() {
- return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size();
+ return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size()
+ + mDnsTlsChecks.size();
}
private void startMeasurements() {
@@ -266,6 +316,9 @@
for (Measurement measurement : mDnsUdpChecks.values()) {
measurement.thread.start();
}
+ for (Measurement measurement : mDnsTlsChecks.values()) {
+ measurement.thread.start();
+ }
}
public void waitForMeasurements() {
@@ -297,6 +350,11 @@
measurements.add(entry.getValue());
}
}
+ for (Map.Entry<InetAddress, Measurement> entry : mDnsTlsChecks.entrySet()) {
+ if (entry.getKey() instanceof Inet4Address) {
+ measurements.add(entry.getValue());
+ }
+ }
// IPv6 measurements second.
for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
@@ -315,6 +373,11 @@
measurements.add(entry.getValue());
}
}
+ for (Map.Entry<InetAddress, Measurement> entry : mDnsTlsChecks.entrySet()) {
+ if (entry.getKey() instanceof Inet6Address) {
+ measurements.add(entry.getValue());
+ }
+ }
return measurements;
}
@@ -387,6 +450,8 @@
try {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
} finally {
+ // TODO: The tag should remain set until all traffic is sent and received.
+ // Consider tagging the socket after the measurement thread is started.
TrafficStats.setThreadStatsTag(oldTag);
}
// Setting SNDTIMEO is purely for defensive purposes.
@@ -403,13 +468,12 @@
mSocketAddress = Os.getsockname(mFileDescriptor);
}
- protected String getSocketAddressString() {
- // The default toString() implementation is not the prettiest.
- InetSocketAddress inetSockAddr = (InetSocketAddress) mSocketAddress;
- InetAddress localAddr = inetSockAddr.getAddress();
- return String.format(
- (localAddr instanceof Inet6Address ? "[%s]:%d" : "%s:%d"),
- localAddr.getHostAddress(), inetSockAddr.getPort());
+ protected boolean ensureMeasurementNecessary() {
+ if (mMeasurement.finishTime == 0) return false;
+
+ // Countdown latch was not decremented when the measurement failed during setup.
+ mCountDownLatch.countDown();
+ return true;
}
@Override
@@ -448,13 +512,7 @@
@Override
public void run() {
- // Check if this measurement has already failed during setup.
- if (mMeasurement.finishTime > 0) {
- // If the measurement failed during construction it didn't
- // decrement the countdown latch; do so here.
- mCountDownLatch.countDown();
- return;
- }
+ if (ensureMeasurementNecessary()) return;
try {
setupSocket(SOCK_DGRAM, mProtocol, TIMEOUT_SEND, TIMEOUT_RECV, 0);
@@ -462,7 +520,7 @@
mMeasurement.recordFailure(e.toString());
return;
}
- mMeasurement.description += " src{" + getSocketAddressString() + "}";
+ mMeasurement.description += " src{" + socketAddressToString(mSocketAddress) + "}";
// Build a trivial ICMP packet.
final byte[] icmpPacket = {
@@ -507,10 +565,10 @@
private static final int RR_TYPE_AAAA = 28;
private static final int PACKET_BUFSIZE = 512;
- private final Random mRandom = new Random();
+ protected final Random mRandom = new Random();
// Should be static, but the compiler mocks our puny, human attempts at reason.
- private String responseCodeStr(int rcode) {
+ protected String responseCodeStr(int rcode) {
try {
return DnsResponseCode.values()[rcode].toString();
} catch (IndexOutOfBoundsException e) {
@@ -518,7 +576,7 @@
}
}
- private final int mQueryType;
+ protected final int mQueryType;
public DnsUdpCheck(InetAddress target, Measurement measurement) {
super(target, measurement);
@@ -535,13 +593,7 @@
@Override
public void run() {
- // Check if this measurement has already failed during setup.
- if (mMeasurement.finishTime > 0) {
- // If the measurement failed during construction it didn't
- // decrement the countdown latch; do so here.
- mCountDownLatch.countDown();
- return;
- }
+ if (ensureMeasurementNecessary()) return;
try {
setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV,
@@ -550,12 +602,10 @@
mMeasurement.recordFailure(e.toString());
return;
}
- mMeasurement.description += " src{" + getSocketAddressString() + "}";
// This needs to be fixed length so it can be dropped into the pre-canned packet.
final String sixRandomDigits = String.valueOf(mRandom.nextInt(900000) + 100000);
- mMeasurement.description += " qtype{" + mQueryType + "}"
- + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
+ appendDnsToMeasurementDescription(sixRandomDigits, mSocketAddress);
// Build a trivial DNS packet.
final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
@@ -592,7 +642,7 @@
close();
}
- private byte[] getDnsQueryPacket(String sixRandomDigits) {
+ protected byte[] getDnsQueryPacket(String sixRandomDigits) {
byte[] rnd = sixRandomDigits.getBytes(StandardCharsets.US_ASCII);
return new byte[] {
(byte) mRandom.nextInt(), (byte) mRandom.nextInt(), // [0-1] query ID
@@ -611,5 +661,97 @@
0, 1 // QCLASS, set to 1 = IN (Internet)
};
}
+
+ protected void appendDnsToMeasurementDescription(
+ String sixRandomDigits, SocketAddress sockAddr) {
+ mMeasurement.description += " src{" + socketAddressToString(sockAddr) + "}"
+ + " qtype{" + mQueryType + "}"
+ + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
+ }
+ }
+
+ // TODO: Have it inherited from SimpleSocketCheck, and separate common DNS helpers out of
+ // DnsUdpCheck.
+ private class DnsTlsCheck extends DnsUdpCheck {
+ private static final int TCP_CONNECT_TIMEOUT_MS = 2500;
+ private static final int TCP_TIMEOUT_MS = 2000;
+ private static final int DNS_TLS_PORT = 853;
+ private static final int DNS_HEADER_SIZE = 12;
+
+ private final String mHostname;
+
+ public DnsTlsCheck(@Nullable String hostname, @NonNull InetAddress target,
+ @NonNull Measurement measurement) {
+ super(target, measurement);
+
+ mHostname = hostname;
+ mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
+ + TextUtils.emptyIfNull(mHostname) + "}";
+ }
+
+ private SSLSocket setupSSLSocket() throws IOException {
+ // A TrustManager will be created and initialized with a KeyStore containing system
+ // CaCerts. During SSL handshake, it will be used to validate the certificates from
+ // the server.
+ SSLSocket sslSocket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
+ sslSocket.setSoTimeout(TCP_TIMEOUT_MS);
+
+ if (!TextUtils.isEmpty(mHostname)) {
+ // Set SNI.
+ final List<SNIServerName> names =
+ Collections.singletonList(new SNIHostName(mHostname));
+ SSLParameters params = sslSocket.getSSLParameters();
+ params.setServerNames(names);
+ sslSocket.setSSLParameters(params);
+ }
+
+ mNetwork.bindSocket(sslSocket);
+ return sslSocket;
+ }
+
+ private void sendDoTProbe(@Nullable SSLSocket sslSocket) throws IOException {
+ final String sixRandomDigits = String.valueOf(mRandom.nextInt(900000) + 100000);
+ final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
+
+ mMeasurement.startTime = now();
+ sslSocket.connect(new InetSocketAddress(mTarget, DNS_TLS_PORT), TCP_CONNECT_TIMEOUT_MS);
+
+ // Synchronous call waiting for the TLS handshake complete.
+ sslSocket.startHandshake();
+ appendDnsToMeasurementDescription(sixRandomDigits, sslSocket.getLocalSocketAddress());
+
+ final DataOutputStream output = new DataOutputStream(sslSocket.getOutputStream());
+ output.writeShort(dnsPacket.length);
+ output.write(dnsPacket, 0, dnsPacket.length);
+
+ final DataInputStream input = new DataInputStream(sslSocket.getInputStream());
+ final int replyLength = Short.toUnsignedInt(input.readShort());
+ final byte[] reply = new byte[replyLength];
+ int bytesRead = 0;
+ while (bytesRead < replyLength) {
+ bytesRead += input.read(reply, bytesRead, replyLength - bytesRead);
+ }
+
+ if (bytesRead > DNS_HEADER_SIZE && bytesRead == replyLength) {
+ mMeasurement.recordSuccess("1/1 " + responseCodeStr((int) (reply[3]) & 0x0f));
+ } else {
+ mMeasurement.recordFailure("1/1 Read " + bytesRead + " bytes while expected to be "
+ + replyLength + " bytes");
+ }
+ }
+
+ @Override
+ public void run() {
+ if (ensureMeasurementNecessary()) return;
+
+ // No need to restore the tag, since this thread is only used for this measurement.
+ TrafficStats.getAndSetThreadStatsTag(TrafficStatsConstants.TAG_SYSTEM_PROBE);
+
+ try (SSLSocket sslSocket = setupSSLSocket()) {
+ sendDoTProbe(sslSocket);
+ } catch (IOException e) {
+ mMeasurement.recordFailure(e.toString());
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 077c405..34b0aa2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -18,6 +18,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.app.Notification;
@@ -27,10 +28,9 @@
import android.content.Intent;
import android.content.res.Resources;
import android.net.NetworkSpecifier;
-import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
import android.os.UserHandle;
-import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -51,9 +51,9 @@
LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
- LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
- SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
+ SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
+ PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
public final int eventId;
@@ -88,15 +88,22 @@
mNotificationTypeMap = new SparseIntArray();
}
+ @VisibleForTesting
+ protected static int approximateTransportType(NetworkAgentInfo nai) {
+ return nai.isVPN() ? TRANSPORT_VPN : getFirstTransportType(nai);
+ }
+
// TODO: deal more gracefully with multi-transport networks.
private static int getFirstTransportType(NetworkAgentInfo nai) {
+ // TODO: The range is wrong, the safer and correct way is to change the range from
+ // MIN_TRANSPORT to MAX_TRANSPORT.
for (int i = 0; i < 64; i++) {
if (nai.networkCapabilities.hasTransport(i)) return i;
}
return -1;
}
- private static String getTransportName(@TransportType int transportType) {
+ private static String getTransportName(final int transportType) {
Resources r = Resources.getSystem();
String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
try {
@@ -106,14 +113,10 @@
}
}
- private static int getIcon(int transportType, NotificationType notifyType) {
- if (transportType != TRANSPORT_WIFI) {
- return R.drawable.stat_notify_rssi_in_range;
- }
-
- return notifyType == NotificationType.LOGGED_IN
- ? R.drawable.ic_wifi_signal_4
- : R.drawable.stat_notify_wifi_in_range; // TODO: Distinguish ! from ?.
+ private static int getIcon(int transportType) {
+ return (transportType == TRANSPORT_WIFI)
+ ? R.drawable.stat_notify_wifi_in_range : // TODO: Distinguish ! from ?.
+ R.drawable.stat_notify_rssi_in_range;
}
/**
@@ -145,9 +148,9 @@
final int transportType;
final String name;
if (nai != null) {
- transportType = getFirstTransportType(nai);
+ transportType = approximateTransportType(nai);
final String extraInfo = nai.networkInfo.getExtraInfo();
- name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo;
+ name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo;
// Only notify for Internet-capable networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
} else {
@@ -174,30 +177,40 @@
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
- Resources r = Resources.getSystem();
- CharSequence title;
- CharSequence details;
- int icon = getIcon(transportType, notifyType);
+ Resources r = mContext.getResources();
+ final CharSequence title;
+ final CharSequence details;
+ int icon = getIcon(transportType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
- WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
details = r.getString(R.string.wifi_no_internet_detailed);
+ } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
+ if (transportType == TRANSPORT_CELLULAR) {
+ title = r.getString(R.string.mobile_no_internet);
+ } else if (transportType == TRANSPORT_WIFI) {
+ title = r.getString(R.string.wifi_no_internet,
+ WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
+ } else {
+ title = r.getString(R.string.other_networks_no_internet);
+ }
+ details = r.getString(R.string.private_dns_broken_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.network_partial_connectivity,
- WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
details = r.getString(R.string.network_partial_connectivity_detailed);
} else if (notifyType == NotificationType.LOST_INTERNET &&
transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
- WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.SIGN_IN) {
switch (transportType) {
case TRANSPORT_WIFI:
title = r.getString(R.string.wifi_available_sign_in, 0);
details = r.getString(R.string.network_available_sign_in_detailed,
- WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
break;
case TRANSPORT_CELLULAR:
title = r.getString(R.string.network_available_sign_in, 0);
@@ -205,14 +218,8 @@
// name has been added to it
NetworkSpecifier specifier = nai.networkCapabilities.getNetworkSpecifier();
int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- if (specifier instanceof StringNetworkSpecifier) {
- try {
- subId = Integer.parseInt(
- ((StringNetworkSpecifier) specifier).specifier);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "NumberFormatException on "
- + ((StringNetworkSpecifier) specifier).specifier);
- }
+ if (specifier instanceof TelephonyNetworkSpecifier) {
+ subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
}
details = mTelephonyManager.createForSubscriptionId(subId)
@@ -223,12 +230,9 @@
details = r.getString(R.string.network_available_sign_in_detailed, name);
break;
}
- } else if (notifyType == NotificationType.LOGGED_IN) {
- title = WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID());
- details = r.getString(R.string.captive_portal_logged_in_detailed);
} else if (notifyType == NotificationType.NETWORK_SWITCH) {
String fromTransport = getTransportName(transportType);
- String toTransport = getTransportName(getFirstTransportType(switchToNai));
+ String toTransport = getTransportName(approximateTransportType(switchToNai));
title = r.getString(R.string.network_switch_metered, toTransport);
details = r.getString(R.string.network_switch_metered_detail, toTransport,
fromTransport);
@@ -329,8 +333,8 @@
}
public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
- String fromTransport = getTransportName(getFirstTransportType(fromNai));
- String toTransport = getTransportName(getFirstTransportType(toNai));
+ String fromTransport = getTransportName(approximateTransportType(fromNai));
+ String toTransport = getTransportName(approximateTransportType(toNai));
String text = mContext.getResources().getString(
R.string.network_switch_metered_toast, fromTransport, toTransport);
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
@@ -357,15 +361,16 @@
}
switch (t) {
case SIGN_IN:
- return 5;
+ return 6;
case PARTIAL_CONNECTIVITY:
+ return 5;
+ case PRIVATE_DNS_BROKEN:
return 4;
case NO_INTERNET:
return 3;
case NETWORK_SWITCH:
return 2;
case LOST_INTERNET:
- case LOGGED_IN:
return 1;
default:
return 0;
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
new file mode 100644
index 0000000..d0aabf9
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkRequest;
+
+import java.util.Collection;
+
+/**
+ * A class that knows how to find the best network matching a request out of a list of networks.
+ */
+public class NetworkRanker {
+ public NetworkRanker() { }
+
+ /**
+ * Find the best network satisfying this request among the list of passed networks.
+ */
+ // Almost equivalent to Collections.max(nais), but allows returning null if no network
+ // satisfies the request.
+ @Nullable
+ public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+ @NonNull final Collection<NetworkAgentInfo> nais) {
+ NetworkAgentInfo bestNetwork = null;
+ int bestScore = Integer.MIN_VALUE;
+ for (final NetworkAgentInfo nai : nais) {
+ if (!nai.satisfies(request)) continue;
+ if (nai.getCurrentScore() > bestScore) {
+ bestNetwork = nai;
+ bestScore = nai.getCurrentScore();
+ }
+ }
+ return bestNetwork;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 56f4959..f0b7150 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
@@ -25,6 +24,7 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -259,7 +259,8 @@
return true;
}
}
- return hasPermission(app, CONNECTIVITY_INTERNAL)
+
+ return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
|| hasPermission(app, NETWORK_STACK)
|| hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
}
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index e570ef1..1129899 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -30,8 +30,8 @@
import static android.system.OsConstants.TIOCOUTQ;
import android.annotation.NonNull;
+import android.net.InvalidPacketException;
import android.net.NetworkUtils;
-import android.net.SocketKeepalive.InvalidPacketException;
import android.net.SocketKeepalive.InvalidSocketException;
import android.net.TcpKeepalivePacketData;
import android.net.TcpKeepalivePacketDataParcelable;
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 306cc51..124b660 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -3,13 +3,61 @@
//########################################################################
java_defaults {
name: "FrameworksNetTests-jni-defaults",
+ jni_libs: [
+ "ld-android",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbpf",
+ "libbpf_android",
+ "libc++",
+ "libcgrouprc",
+ "libcrypto",
+ "libcutils",
+ "libdl_android",
+ "libhidl-gen-utils",
+ "libhidlbase",
+ "libjsoncpp",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdbpf",
+ "libnetdutils",
+ "libnetworkstatsfactorytestjni",
+ "libpackagelistparser",
+ "libpcre2",
+ "libprocessgroup",
+ "libselinux",
+ "libtinyxml2",
+ "libui",
+ "libunwindstack",
+ "libutils",
+ "libutilscallstack",
+ "libvndksupport",
+ "libziparchive",
+ "libz",
+ "netd_aidl_interface-cpp",
+ ],
+}
+
+android_test {
+ name: "FrameworksNetTests",
+ defaults: ["FrameworksNetTests-jni-defaults"],
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ certificate: "platform",
static_libs: [
+ "androidx.test.rules",
"FrameworksNetCommonTests",
"frameworks-base-testutils",
- "frameworks-net-testutils",
+ "frameworks-net-integration-testutils",
"framework-protos",
- "androidx.test.rules",
"mockito-target-minus-junit4",
+ "net-tests-utils",
"platform-test-annotations",
"services.core",
"services.net",
@@ -19,53 +67,4 @@
"android.test.base",
"android.test.mock",
],
- jni_libs: [
- "ld-android",
- "libartbase",
- "libbacktrace",
- "libbase",
- "libbinder",
- "libbinderthreadstate",
- "libbpf",
- "libbpf_android",
- "libc++",
- "libcgrouprc",
- "libcrypto",
- "libcutils",
- "libdexfile",
- "libdl_android",
- "libhidl-gen-utils",
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libjsoncpp",
- "liblog",
- "liblzma",
- "libnativehelper",
- "libnetdbpf",
- "libnetdutils",
- "libpackagelistparser",
- "libpcre2",
- "libprocessgroup",
- "libselinux",
- "libui",
- "libutils",
- "libvndksupport",
- "libtinyxml2",
- "libunwindstack",
- "libutilscallstack",
- "libziparchive",
- "libz",
- "netd_aidl_interface-V2-cpp",
- "libnetworkstatsfactorytestjni",
- ],
-}
-
-android_test {
- name: "FrameworksNetTests",
- defaults: ["FrameworksNetTests-jni-defaults"],
- srcs: ["java/**/*.java", "java/**/*.kt"],
- platform_apis: true,
- test_suites: ["device-tests"],
- certificate: "platform",
}
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
new file mode 100644
index 0000000..005cbe9
--- /dev/null
+++ b/tests/net/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksNetIntegrationTests"
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksNetDeflakeTest"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index db1ccb4..46d680f 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -20,13 +20,14 @@
name: "FrameworksNetCommonTests",
srcs: ["java/**/*.java", "java/**/*.kt"],
static_libs: [
+ "androidx.core_core",
"androidx.test.rules",
- "frameworks-net-testutils",
"junit",
"mockito-target-minus-junit4",
+ "net-tests-utils",
"platform-test-annotations",
],
libs: [
"android.test.base.stubs",
],
-}
\ No newline at end of file
+}
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
new file mode 100644
index 0000000..bd1847b
--- /dev/null
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class CaptivePortalDataTest {
+ private val data = CaptivePortalData.Builder()
+ .setRefreshTime(123L)
+ .setUserPortalUrl(Uri.parse("https://portal.example.com/test"))
+ .setVenueInfoUrl(Uri.parse("https://venue.example.com/test"))
+ .setSessionExtendable(true)
+ .setBytesRemaining(456L)
+ .setExpiryTime(789L)
+ .setCaptive(true)
+ .build()
+
+ private fun makeBuilder() = CaptivePortalData.Builder(data)
+
+ @Test
+ fun testParcelUnparcel() {
+ assertParcelSane(data, fieldCount = 7)
+
+ assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
+ assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
+ }
+
+ @Test
+ fun testEquals() {
+ assertEquals(data, makeBuilder().build())
+
+ assertNotEqualsAfterChange { it.setRefreshTime(456L) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(null) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(null) }
+ assertNotEqualsAfterChange { it.setSessionExtendable(false) }
+ assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
+ assertNotEqualsAfterChange { it.setExpiryTime(12L) }
+ assertNotEqualsAfterChange { it.setCaptive(false) }
+ }
+
+ @Test
+ fun testUserPortalUrl() {
+ assertEquals(Uri.parse("https://portal.example.com/test"), data.userPortalUrl)
+ }
+
+ @Test
+ fun testVenueInfoUrl() {
+ assertEquals(Uri.parse("https://venue.example.com/test"), data.venueInfoUrl)
+ }
+
+ @Test
+ fun testIsSessionExtendable() {
+ assertTrue(data.isSessionExtendable)
+ }
+
+ @Test
+ fun testByteLimit() {
+ assertEquals(456L, data.byteLimit)
+ // Test byteLimit unset.
+ assertEquals(-1L, CaptivePortalData.Builder(null).build().byteLimit)
+ }
+
+ @Test
+ fun testRefreshTimeMillis() {
+ assertEquals(123L, data.refreshTimeMillis)
+ }
+
+ @Test
+ fun testExpiryTimeMillis() {
+ assertEquals(789L, data.expiryTimeMillis)
+ // Test expiryTimeMillis unset.
+ assertEquals(-1L, CaptivePortalData.Builder(null).build().expiryTimeMillis)
+ }
+
+ @Test
+ fun testIsCaptive() {
+ assertTrue(data.isCaptive)
+ assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
+ }
+
+ private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
+ CaptivePortalData.Builder(this).apply { mutator(this) }.build()
+
+ private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) {
+ assertNotEquals(data, data.mutate(mutator))
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java
index eed7159..7a60cc1 100644
--- a/tests/net/common/java/android/net/CaptivePortalTest.java
+++ b/tests/net/common/java/android/net/CaptivePortalTest.java
@@ -18,19 +18,26 @@
import static org.junit.Assert.assertEquals;
+import android.os.Build;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CaptivePortalTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final int DEFAULT_TIMEOUT_MS = 5000;
private static final String TEST_PACKAGE_NAME = "com.google.android.test";
@@ -44,6 +51,11 @@
}
@Override
+ public void appRequest(final int request) throws RemoteException {
+ mCode = request;
+ }
+
+ @Override
public void logEvent(int eventId, String packageName) throws RemoteException {
mCode = eventId;
mPackageName = packageName;
@@ -79,6 +91,13 @@
assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS);
}
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
+ public void testReevaluateNetwork() {
+ final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork());
+ assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED);
+ }
+
@Test
public void testLogEvent() {
final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent(
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/tests/net/common/java/android/net/DependenciesTest.java
new file mode 100644
index 0000000..ac1c28a
--- /dev/null
+++ b/tests/net/common/java/android/net/DependenciesTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple class that tests dependencies to java standard tools from the
+ * Network stack. These tests are not meant to be comprehensive tests of
+ * the relevant APIs : such tests belong in the relevant test suite for
+ * these dependencies. Instead, this just makes sure coverage is present
+ * by calling the methods in the exact way (or a representative way of how)
+ * they are called in the network stack.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DependenciesTest {
+ // Used to in ipmemorystore's RegularMaintenanceJobService to convert
+ // 24 hours into seconds
+ @Test
+ public void testTimeUnit() {
+ final int hours = 24;
+ final long inSeconds = TimeUnit.HOURS.toMillis(hours);
+ assertEquals(inSeconds, hours * 60 * 60 * 1000);
+ }
+
+ private byte[] makeTrivialArray(final int size) {
+ final byte[] src = new byte[size];
+ for (int i = 0; i < size; ++i) {
+ src[i] = (byte) i;
+ }
+ return src;
+ }
+
+ // Used in ApfFilter to find an IP address from a byte array
+ @Test
+ public void testArrays() {
+ final int size = 128;
+ final byte[] src = makeTrivialArray(size);
+
+ // Test copy
+ final int copySize = 16;
+ final int offset = 24;
+ final byte[] expected = new byte[copySize];
+ for (int i = 0; i < copySize; ++i) {
+ expected[i] = (byte) (offset + i);
+ }
+
+ final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize);
+ assertArrayEquals(expected, copy);
+ assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size));
+ }
+
+ // Used mainly in the Dhcp code
+ @Test
+ public void testCopyOf() {
+ final byte[] src = makeTrivialArray(128);
+ final byte[] copy = Arrays.copyOf(src, src.length);
+ assertArrayEquals(src, copy);
+ assertFalse(src == copy);
+
+ assertArrayEquals(new byte[0], Arrays.copyOf(src, 0));
+
+ final int excess = 16;
+ final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess);
+ for (int i = src.length; i < src.length + excess; ++i) {
+ assertEquals(0, biggerCopy[i]);
+ }
+ for (int i = src.length - 1; i >= 0; --i) {
+ assertEquals(src[i], biggerCopy[i]);
+ }
+ }
+
+ // Used mainly in DnsUtils but also various other places
+ @Test
+ public void testAsList() {
+ final int size = 24;
+ final Object[] src = new Object[size];
+ final ArrayList<Object> expected = new ArrayList<>(size);
+ for (int i = 0; i < size; ++i) {
+ final Object o = new Object();
+ src[i] = o;
+ expected.add(o);
+ }
+ assertEquals(expected, Arrays.asList(src));
+ }
+}
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java
new file mode 100644
index 0000000..4d45ad7
--- /dev/null
+++ b/tests/net/common/java/android/net/DhcpInfoTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 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 static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+public class DhcpInfoTest {
+ private static final String STR_ADDR1 = "255.255.255.255";
+ private static final String STR_ADDR2 = "127.0.0.1";
+ private static final String STR_ADDR3 = "192.168.1.1";
+ private static final String STR_ADDR4 = "192.168.1.0";
+ private static final int LEASE_TIME = 9999;
+
+ private int ipToInteger(String ipString) throws Exception {
+ return inet4AddressToIntHTL((Inet4Address) InetAddress.getByName(ipString));
+ }
+
+ private DhcpInfo createDhcpInfoObject() throws Exception {
+ final DhcpInfo dhcpInfo = new DhcpInfo();
+ dhcpInfo.ipAddress = ipToInteger(STR_ADDR1);
+ dhcpInfo.gateway = ipToInteger(STR_ADDR2);
+ dhcpInfo.netmask = ipToInteger(STR_ADDR3);
+ dhcpInfo.dns1 = ipToInteger(STR_ADDR4);
+ dhcpInfo.dns2 = ipToInteger(STR_ADDR4);
+ dhcpInfo.serverAddress = ipToInteger(STR_ADDR2);
+ dhcpInfo.leaseDuration = LEASE_TIME;
+ return dhcpInfo;
+ }
+
+ @Test
+ public void testConstructor() {
+ new DhcpInfo();
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ final String expectedDefault = "ipaddr 0.0.0.0 gateway 0.0.0.0 netmask 0.0.0.0 "
+ + "dns1 0.0.0.0 dns2 0.0.0.0 DHCP server 0.0.0.0 lease 0 seconds";
+
+ DhcpInfo dhcpInfo = new DhcpInfo();
+
+ // Test default string.
+ assertEquals(expectedDefault, dhcpInfo.toString());
+
+ dhcpInfo = createDhcpInfoObject();
+
+ final String expected = "ipaddr " + STR_ADDR1 + " gateway " + STR_ADDR2 + " netmask "
+ + STR_ADDR3 + " dns1 " + STR_ADDR4 + " dns2 " + STR_ADDR4 + " DHCP server "
+ + STR_ADDR2 + " lease " + LEASE_TIME + " seconds";
+ // Test with new values
+ assertEquals(expected, dhcpInfo.toString());
+ }
+
+ private boolean dhcpInfoEquals(@Nullable DhcpInfo left, @Nullable DhcpInfo right) {
+ if (left == null && right == null) return true;
+
+ if (left == null || right == null) return false;
+
+ return left.ipAddress == right.ipAddress
+ && left.gateway == right.gateway
+ && left.netmask == right.netmask
+ && left.dns1 == right.dns1
+ && left.dns2 == right.dns2
+ && left.serverAddress == right.serverAddress
+ && left.leaseDuration == right.leaseDuration;
+ }
+
+ @Test
+ public void testParcelDhcpInfo() throws Exception {
+ // Cannot use assertParcelSane() here because this requires .equals() to work as
+ // defined, but DhcpInfo has a different legacy behavior that we cannot change.
+ final DhcpInfo dhcpInfo = createDhcpInfoObject();
+ assertFieldCountEquals(7, DhcpInfo.class);
+
+ final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo);
+ assertTrue(dhcpInfoEquals(null, null));
+ assertFalse(dhcpInfoEquals(null, dhcpInfoRoundTrip));
+ assertFalse(dhcpInfoEquals(dhcpInfo, null));
+ assertTrue(dhcpInfoEquals(dhcpInfo, dhcpInfoRoundTrip));
+ }
+}
diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java
index 719960d..985e10d 100644
--- a/tests/net/common/java/android/net/IpPrefixTest.java
+++ b/tests/net/common/java/android/net/IpPrefixTest.java
@@ -16,16 +16,18 @@
package android.net;
+import static com.android.testutils.MiscAssertsKt.assertEqualBothWays;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.os.Parcel;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -171,56 +173,46 @@
}
- private void assertAreEqual(Object o1, Object o2) {
- assertTrue(o1.equals(o2));
- assertTrue(o2.equals(o1));
- }
-
- private void assertAreNotEqual(Object o1, Object o2) {
- assertFalse(o1.equals(o2));
- assertFalse(o2.equals(o1));
- }
-
@Test
public void testEquals() {
IpPrefix p1, p2;
p1 = new IpPrefix("192.0.2.251/23");
p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
- assertAreEqual(p1, p2);
+ assertEqualBothWays(p1, p2);
p1 = new IpPrefix("192.0.2.5/23");
- assertAreEqual(p1, p2);
+ assertEqualBothWays(p1, p2);
p1 = new IpPrefix("192.0.2.5/24");
- assertAreNotEqual(p1, p2);
+ assertNotEqualEitherWay(p1, p2);
p1 = new IpPrefix("192.0.4.5/23");
- assertAreNotEqual(p1, p2);
+ assertNotEqualEitherWay(p1, p2);
p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
p2 = new IpPrefix(IPV6_BYTES, 122);
assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
- assertAreEqual(p1, p2);
+ assertEqualBothWays(p1, p2);
p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
- assertAreEqual(p1, p2);
+ assertEqualBothWays(p1, p2);
p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
- assertAreNotEqual(p1, p2);
+ assertNotEqualEitherWay(p1, p2);
p1 = new IpPrefix("2001:db8:dead:beef::/122");
- assertAreNotEqual(p1, p2);
+ assertNotEqualEitherWay(p1, p2);
// 192.0.2.4/32 != c000:0204::/32.
byte[] ipv6bytes = new byte[16];
System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
p1 = new IpPrefix(ipv6bytes, 32);
- assertAreEqual(p1, new IpPrefix("c000:0204::/32"));
+ assertEqualBothWays(p1, new IpPrefix("c000:0204::/32"));
p2 = new IpPrefix(IPV4_BYTES, 32);
- assertAreNotEqual(p1, p2);
+ assertNotEqualEitherWay(p1, p2);
}
@Test
@@ -356,25 +348,6 @@
assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
}
- public IpPrefix passThroughParcel(IpPrefix p) {
- Parcel parcel = Parcel.obtain();
- IpPrefix p2 = null;
- try {
- p.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- p2 = IpPrefix.CREATOR.createFromParcel(parcel);
- } finally {
- parcel.recycle();
- }
- assertNotNull(p2);
- return p2;
- }
-
- public void assertParcelingIsLossless(IpPrefix p) {
- IpPrefix p2 = passThroughParcel(p);
- assertEquals(p, p2);
- }
-
@Test
public void testParceling() {
IpPrefix p;
@@ -386,5 +359,7 @@
p = new IpPrefix("192.0.2.0/25");
assertParcelingIsLossless(p);
assertTrue(p.isIPv4());
+
+ assertFieldCountEquals(2, IpPrefix.class);
}
}
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
new file mode 100644
index 0000000..f464ec6
--- /dev/null
+++ b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import java.net.InetAddress
+import java.util.Arrays
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class KeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ private val INVALID_PORT = 65537
+ private val TEST_DST_PORT = 4244
+ private val TEST_SRC_PORT = 4243
+
+ private val TESTBYTES = byteArrayOf(12, 31, 22, 44)
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+
+ // Add for test because constructor of KeepalivePacketData is protected.
+ private inner class TestKeepalivePacketData(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_SRC_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = TEST_DST_PORT,
+ data: ByteArray = TESTBYTES
+ ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ var data: TestKeepalivePacketData
+
+ try {
+ data = TestKeepalivePacketData(srcAddress = null)
+ fail("Null src address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = null)
+ fail("Null dst address should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
+ fail("Ip family mismatched should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ data = TestKeepalivePacketData(srcPort = INVALID_PORT)
+ fail("Invalid srcPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ data = TestKeepalivePacketData(dstPort = INVALID_PORT)
+ fail("Invalid dstPort should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort)
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet))
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index d462441..c74c112 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -27,18 +27,28 @@
import static android.system.OsConstants.RT_SCOPE_SITE;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import static com.android.testutils.MiscAssertsKt.assertEqualBothWays;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.os.Parcel;
+import android.os.Build;
+import android.os.SystemClock;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +63,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LinkAddressTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
private static final String V4 = "192.0.2.1";
private static final String V6 = "2001:db8::1";
@@ -217,67 +229,56 @@
l1.isSameAddressAs(l2));
}
- private void assertLinkAddressesEqual(LinkAddress l1, LinkAddress l2) {
- assertTrue(l1 + " unexpectedly not equal to " + l2, l1.equals(l2));
- assertTrue(l2 + " unexpectedly not equal to " + l1, l2.equals(l1));
- assertEquals(l1.hashCode(), l2.hashCode());
- }
-
- private void assertLinkAddressesNotEqual(LinkAddress l1, LinkAddress l2) {
- assertFalse(l1 + " unexpectedly equal to " + l2, l1.equals(l2));
- assertFalse(l2 + " unexpectedly equal to " + l1, l2.equals(l1));
- }
-
@Test
public void testEqualsAndSameAddressAs() {
LinkAddress l1, l2, l3;
l1 = new LinkAddress("2001:db8::1/64");
l2 = new LinkAddress("2001:db8::1/64");
- assertLinkAddressesEqual(l1, l2);
+ assertEqualBothWays(l1, l2);
assertIsSameAddressAs(l1, l2);
l2 = new LinkAddress("2001:db8::1/65");
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsNotSameAddressAs(l1, l2);
l2 = new LinkAddress("2001:db8::2/64");
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsNotSameAddressAs(l1, l2);
l1 = new LinkAddress("192.0.2.1/24");
l2 = new LinkAddress("192.0.2.1/24");
- assertLinkAddressesEqual(l1, l2);
+ assertEqualBothWays(l1, l2);
assertIsSameAddressAs(l1, l2);
l2 = new LinkAddress("192.0.2.1/23");
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsNotSameAddressAs(l1, l2);
l2 = new LinkAddress("192.0.2.2/24");
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsNotSameAddressAs(l1, l2);
// Check equals() and isSameAddressAs() on identical addresses with different flags.
l1 = new LinkAddress(V6_ADDRESS, 64);
l2 = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE);
- assertLinkAddressesEqual(l1, l2);
+ assertEqualBothWays(l1, l2);
assertIsSameAddressAs(l1, l2);
l2 = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE);
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsSameAddressAs(l1, l2);
// Check equals() and isSameAddressAs() on identical addresses with different scope.
l1 = new LinkAddress(V4_ADDRESS, 24);
l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_UNIVERSE);
- assertLinkAddressesEqual(l1, l2);
+ assertEqualBothWays(l1, l2);
assertIsSameAddressAs(l1, l2);
l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_HOST);
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsSameAddressAs(l1, l2);
// Addresses with the same start or end bytes aren't equal between families.
@@ -291,10 +292,10 @@
assertTrue(Arrays.equals(ipv4Bytes, l2FirstIPv6Bytes));
assertTrue(Arrays.equals(ipv4Bytes, l3LastIPv6Bytes));
- assertLinkAddressesNotEqual(l1, l2);
+ assertNotEqualEitherWay(l1, l2);
assertIsNotSameAddressAs(l1, l2);
- assertLinkAddressesNotEqual(l1, l3);
+ assertNotEqualEitherWay(l1, l3);
assertIsNotSameAddressAs(l1, l3);
// Because we use InetAddress, an IPv4 address is equal to its IPv4-mapped address.
@@ -302,7 +303,7 @@
String addressString = V4 + "/24";
l1 = new LinkAddress(addressString);
l2 = new LinkAddress("::ffff:" + addressString);
- assertLinkAddressesEqual(l1, l2);
+ assertEqualBothWays(l1, l2);
assertIsSameAddressAs(l1, l2);
}
@@ -319,25 +320,6 @@
assertNotEquals(l1.hashCode(), l2.hashCode());
}
- private LinkAddress passThroughParcel(LinkAddress l) {
- Parcel p = Parcel.obtain();
- LinkAddress l2 = null;
- try {
- l.writeToParcel(p, 0);
- p.setDataPosition(0);
- l2 = LinkAddress.CREATOR.createFromParcel(p);
- } finally {
- p.recycle();
- }
- assertNotNull(l2);
- return l2;
- }
-
- private void assertParcelingIsLossless(LinkAddress l) {
- LinkAddress l2 = passThroughParcel(l);
- assertEquals(l, l2);
- }
-
@Test
public void testParceling() {
LinkAddress l;
@@ -349,6 +331,96 @@
assertParcelingIsLossless(l);
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testLifetimeParceling() {
+ final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, 456, 1L, 3600000L);
+ assertParcelingIsLossless(l);
+ }
+
+ @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+ public void testFieldCount_Q() {
+ assertFieldCountEquals(4, LinkAddress.class);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testFieldCount() {
+ // Make sure any new field is covered by the above parceling tests when changing this number
+ assertFieldCountEquals(6, LinkAddress.class);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testDeprecationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ LinkAddress.LIFETIME_UNKNOWN, 100000L);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ 200000L, 100000L);
+ fail("deprecation time later than expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ -2, 100000L);
+ fail("negative deprecation time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(100000L, addr.getDeprecationTime());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testExpirationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ 200000L, LinkAddress.LIFETIME_UNKNOWN);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ 100000L, -2);
+ fail("negative expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(200000L, addr.getExpirationTime());
+ }
+
+ @Test
+ public void testGetFlags() {
+ LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST);
+ assertEquals(123, l.getFlags());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testGetFlags_Deprecation() {
+ // Test if deprecated bit was added/remove automatically based on the provided deprecation
+ // time
+ LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST,
+ 1L, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is added automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() + 100000L, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is removed automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the permanent flag is added.
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST,
+ 1000L, SystemClock.elapsedRealtime() + 100000L);
+ // Check if the permanent flag is removed
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0);
+ }
+
private void assertGlobalPreferred(LinkAddress l, String msg) {
assertTrue(msg, l.isGlobalPreferred());
}
@@ -418,4 +490,14 @@
RT_SCOPE_UNIVERSE);
assertGlobalPreferred(l, "v6,global,tempaddr+optimistic");
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testIsGlobalPreferred_DeprecatedInFuture() {
+ final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED,
+ RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000,
+ SystemClock.elapsedRealtime() + 200000);
+ // Although the deprecated bit is set, but the deprecation time is in the future, test
+ // if the flag is removed automatically.
+ assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future");
+ }
}
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 388e9eb..6eba62e 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -16,6 +16,14 @@
package android.net;
+import static android.net.RouteInfo.RTN_THROW;
+import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
+import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -23,20 +31,26 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.LinkProperties.CompareResult;
import android.net.LinkProperties.ProvisioningChange;
+import android.net.util.LinkPropertiesUtils.CompareResult;
+import android.os.Build;
import android.system.OsConstants;
import android.util.ArraySet;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.ParcelableTestUtil;
-import com.android.internal.util.TestUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,6 +62,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LinkPropertiesTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final InetAddress ADDRV4 = address("75.208.6.1");
private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
private static final InetAddress DNS1 = address("75.208.7.1");
@@ -64,6 +81,7 @@
private static final InetAddress GATEWAY62 = address("fe80::6:22%lo");
private static final InetAddress TESTIPV4ADDR = address("192.168.47.42");
private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43");
+ private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1");
private static final String NAME = "qmi0";
private static final String DOMAINS = "google.com";
private static final String PRIV_DNS_SERVER_NAME = "private.dns.com";
@@ -72,11 +90,24 @@
private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
+
+ // CaptivePortalData cannot be in a constant as it does not exist on Q.
+ // The test runner also crashes when scanning for tests if it is a return type.
+ private static Object getCaptivePortalData() {
+ return new CaptivePortalData.Builder()
+ .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+ }
private static InetAddress address(String addrString) {
return InetAddresses.parseNumericAddress(addrString);
}
+ private static boolean isAtLeastR() {
+ // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+ }
+
private void checkEmpty(final LinkProperties lp) {
assertEquals(0, lp.getAllInterfaceNames().size());
assertEquals(0, lp.getAllAddresses().size());
@@ -96,6 +127,13 @@
assertFalse(lp.isIpv4Provisioned());
assertFalse(lp.isIpv6Provisioned());
assertFalse(lp.isPrivateDnsActive());
+
+ if (isAtLeastR()) {
+ assertNull(lp.getDhcpServerAddress());
+ assertFalse(lp.isWakeOnLanSupported());
+ assertNull(lp.getCaptivePortalApiUrl());
+ assertNull(lp.getCaptivePortalData());
+ }
}
private LinkProperties makeTestObject() {
@@ -117,6 +155,12 @@
lp.setMtu(MTU);
lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ if (isAtLeastR()) {
+ lp.setDhcpServerAddress(DHCPSERVER);
+ lp.setWakeOnLanSupported(true);
+ lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+ }
return lp;
}
@@ -155,6 +199,20 @@
assertTrue(source.isIdenticalTcpBufferSizes(target));
assertTrue(target.isIdenticalTcpBufferSizes(source));
+ if (isAtLeastR()) {
+ assertTrue(source.isIdenticalDhcpServerAddress(target));
+ assertTrue(source.isIdenticalDhcpServerAddress(source));
+
+ assertTrue(source.isIdenticalWakeOnLan(target));
+ assertTrue(target.isIdenticalWakeOnLan(source));
+
+ assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+ assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+ assertTrue(source.isIdenticalCaptivePortalData(target));
+ assertTrue(target.isIdenticalCaptivePortalData(source));
+ }
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
@@ -292,7 +350,7 @@
source.addDnsServer(DNS1);
source.addDnsServer(DNS2);
// set 2 gateways
- source.addRoute(new RouteInfo(GATEWAY1));
+ source.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
source.setMtu(MTU);
@@ -304,7 +362,7 @@
target.addDnsServer(DNS2);
target.addDnsServer(DNS1);
target.addRoute(new RouteInfo(GATEWAY2));
- target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
target.setMtu(MTU);
assertLinkPropertiesEqual(source, target);
@@ -341,12 +399,13 @@
@Test
public void testRouteInterfaces() {
- LinkAddress prefix = new LinkAddress(address("2001:db8::"), 32);
+ LinkAddress prefix1 = new LinkAddress(address("2001:db8:1::"), 48);
+ LinkAddress prefix2 = new LinkAddress(address("2001:db8:2::"), 48);
InetAddress address = ADDRV6;
// Add a route with no interface to a LinkProperties with no interface. No errors.
LinkProperties lp = new LinkProperties();
- RouteInfo r = new RouteInfo(prefix, address, null);
+ RouteInfo r = new RouteInfo(prefix1, address, null);
assertTrue(lp.addRoute(r));
assertEquals(1, lp.getRoutes().size());
assertAllRoutesHaveInterface(null, lp);
@@ -356,7 +415,7 @@
assertEquals(1, lp.getRoutes().size());
// Add a route with an interface. Expect an exception.
- r = new RouteInfo(prefix, address, "wlan0");
+ r = new RouteInfo(prefix2, address, "wlan0");
try {
lp.addRoute(r);
fail("Adding wlan0 route to LP with no interface, expect exception");
@@ -375,7 +434,7 @@
} catch (IllegalArgumentException expected) {}
// If the interface name matches, the route is added.
- r = new RouteInfo(prefix, null, "wlan0");
+ r = new RouteInfo(prefix2, null, "wlan0");
lp.setInterfaceName("wlan0");
lp.addRoute(r);
assertEquals(2, lp.getRoutes().size());
@@ -391,19 +450,27 @@
// Check comparisons work.
LinkProperties lp2 = new LinkProperties(lp);
assertAllRoutesHaveInterface("wlan0", lp2);
- assertEquals(0, lp.compareAllRoutes(lp2).added.size());
- assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+ // LinkProperties#compareAllRoutes exists both in R and before R, but the return type
+ // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q.
+ if (isAtLeastR()) {
+ assertEquals(0, lp.compareAllRoutes(lp2).added.size());
+ assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+ }
lp2.setInterfaceName("p2p0");
assertAllRoutesHaveInterface("p2p0", lp2);
assertAllRoutesNotHaveInterface("wlan0", lp2);
- assertEquals(3, lp.compareAllRoutes(lp2).added.size());
- assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+ if (isAtLeastR()) {
+ assertEquals(3, lp.compareAllRoutes(lp2).added.size());
+ assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+ }
- // Check remove works
- lp.removeRoute(new RouteInfo(prefix, address, null));
+ // Remove route with incorrect interface, no route removed.
+ lp.removeRoute(new RouteInfo(prefix2, null, null));
assertEquals(3, lp.getRoutes().size());
- lp.removeRoute(new RouteInfo(prefix, address, "wlan0"));
+
+ // Check remove works when interface is correct.
+ lp.removeRoute(new RouteInfo(prefix2, null, "wlan0"));
assertEquals(2, lp.getRoutes().size());
assertAllRoutesHaveInterface("wlan0", lp);
assertAllRoutesNotHaveInterface("p2p0", lp);
@@ -424,6 +491,8 @@
assertEquals(1, rmnet0.getLinkAddresses().size());
assertEquals(1, rmnet0.getAllAddresses().size());
assertEquals(1, rmnet0.getAllLinkAddresses().size());
+ assertEquals(1, rmnet0.getAllInterfaceNames().size());
+ assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
rmnet0.addStackedLink(clat4);
assertEquals(1, rmnet0.getStackedLinks().size());
@@ -431,6 +500,9 @@
assertEquals(1, rmnet0.getLinkAddresses().size());
assertEquals(2, rmnet0.getAllAddresses().size());
assertEquals(2, rmnet0.getAllLinkAddresses().size());
+ assertEquals(2, rmnet0.getAllInterfaceNames().size());
+ assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
+ assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1));
rmnet0.addStackedLink(clat4);
assertEquals(1, rmnet0.getStackedLinks().size());
@@ -438,6 +510,9 @@
assertEquals(1, rmnet0.getLinkAddresses().size());
assertEquals(2, rmnet0.getAllAddresses().size());
assertEquals(2, rmnet0.getAllLinkAddresses().size());
+ assertEquals(2, rmnet0.getAllInterfaceNames().size());
+ assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
+ assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1));
assertEquals(0, clat4.getStackedLinks().size());
@@ -457,6 +532,8 @@
assertEquals(1, rmnet0.getLinkAddresses().size());
assertEquals(1, rmnet0.getAllAddresses().size());
assertEquals(1, rmnet0.getAllLinkAddresses().size());
+ assertEquals(1, rmnet0.getAllInterfaceNames().size());
+ assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
assertFalse(rmnet0.removeStackedLink("clat4"));
}
@@ -880,7 +957,7 @@
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testCompareResult() {
// Either adding or removing items
compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
@@ -917,8 +994,7 @@
assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
}
- @Test
- public void testLinkPropertiesParcelable() throws Exception {
+ private static LinkProperties makeLinkPropertiesForParceling() {
LinkProperties source = new LinkProperties();
source.setInterfaceName(NAME);
@@ -956,16 +1032,40 @@
stacked.setInterfaceName("test-stacked");
source.addStackedLink(stacked);
- TestUtils.assertParcelingIsLossless(source);
- ParcelableTestUtil.assertFieldCountEquals(14, LinkProperties.class);
+ return source;
}
- @Test
+ @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+ public void testLinkPropertiesParcelable_Q() throws Exception {
+ final LinkProperties source = makeLinkPropertiesForParceling();
+ assertParcelSane(source, 14 /* fieldCount */);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testLinkPropertiesParcelable() throws Exception {
+ final LinkProperties source = makeLinkPropertiesForParceling();
+
+ source.setWakeOnLanSupported(true);
+ source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+ source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+ assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */),
+ 18 /* fieldCount */);
+
+ // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
+ final LinkProperties sanitized = new LinkProperties(source);
+ sanitized.setCaptivePortalApiUrl(null);
+ sanitized.setCaptivePortalData(null);
+ assertEquals(sanitized, parcelingRoundTrip(source));
+ }
+
+ // Parceling of the scope was broken until Q-QPR2
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testLinkLocalDnsServerParceling() throws Exception {
final String strAddress = "fe80::1%lo";
final LinkProperties lp = new LinkProperties();
lp.addDnsServer(address(strAddress));
- final LinkProperties unparceled = TestUtils.parcelingRoundTrip(lp);
+ final LinkProperties unparceled = parcelingRoundTrip(lp);
// Inet6Address#equals does not test for the scope id
assertEquals(strAddress, unparceled.getDnsServers().get(0).getHostAddress());
}
@@ -973,7 +1073,7 @@
@Test
public void testParcelUninitialized() throws Exception {
LinkProperties empty = new LinkProperties();
- TestUtils.assertParcelingIsLossless(empty);
+ assertParcelingIsLossless(empty);
}
@Test
@@ -1082,4 +1182,122 @@
lp.clear();
assertFalse(lp.isPrivateDnsActive());
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testDhcpServerAddress() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
+
+ lp.clear();
+ assertNull(lp.getDhcpServerAddress());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testWakeOnLanSupported() {
+ final LinkProperties lp = makeTestObject();
+ assertTrue(lp.isWakeOnLanSupported());
+
+ lp.clear();
+ assertFalse(lp.isWakeOnLanSupported());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testCaptivePortalApiUrl() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
+
+ lp.clear();
+ assertNull(lp.getCaptivePortalApiUrl());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testCaptivePortalData() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(getCaptivePortalData(), lp.getCaptivePortalData());
+
+ lp.clear();
+ assertNull(lp.getCaptivePortalData());
+ }
+
+ private LinkProperties makeIpv4LinkProperties() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(NAME);
+ linkProperties.addLinkAddress(LINKADDRV4);
+ linkProperties.addDnsServer(DNS1);
+ linkProperties.addRoute(new RouteInfo(GATEWAY1));
+ linkProperties.addRoute(new RouteInfo(GATEWAY2));
+ return linkProperties;
+ }
+
+ private LinkProperties makeIpv6LinkProperties() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(NAME);
+ linkProperties.addLinkAddress(LINKADDRV6);
+ linkProperties.addDnsServer(DNS6);
+ linkProperties.addRoute(new RouteInfo(GATEWAY61));
+ linkProperties.addRoute(new RouteInfo(GATEWAY62));
+ return linkProperties;
+ }
+
+ @Test
+ public void testHasIpv4DefaultRoute() {
+ final LinkProperties Ipv4 = makeIpv4LinkProperties();
+ assertTrue(Ipv4.hasIpv4DefaultRoute());
+ final LinkProperties Ipv6 = makeIpv6LinkProperties();
+ assertFalse(Ipv6.hasIpv4DefaultRoute());
+ }
+
+ @Test
+ public void testHasIpv4DnsServer() {
+ final LinkProperties Ipv4 = makeIpv4LinkProperties();
+ assertTrue(Ipv4.hasIpv4DnsServer());
+ final LinkProperties Ipv6 = makeIpv6LinkProperties();
+ assertFalse(Ipv6.hasIpv4DnsServer());
+ }
+
+ @Test
+ public void testHasIpv6DnsServer() {
+ final LinkProperties Ipv4 = makeIpv4LinkProperties();
+ assertFalse(Ipv4.hasIpv6DnsServer());
+ final LinkProperties Ipv6 = makeIpv6LinkProperties();
+ assertTrue(Ipv6.hasIpv6DnsServer());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testHasIpv4UnreachableDefaultRoute() {
+ final LinkProperties lp = makeTestObject();
+ assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+ assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ assertTrue(lp.hasIpv4UnreachableDefaultRoute());
+ assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testHasIpv6UnreachableDefaultRoute() {
+ final LinkProperties lp = makeTestObject();
+ assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+ assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ assertTrue(lp.hasIpv6UnreachableDefaultRoute());
+ assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testRouteAddWithSameKey() throws Exception {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan0");
+ final IpPrefix v6 = new IpPrefix("64:ff9b::/96");
+ lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1280));
+ assertEquals(1, lp.getRoutes().size());
+ lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1500));
+ assertEquals(1, lp.getRoutes().size());
+ final IpPrefix v4 = new IpPrefix("192.0.2.128/25");
+ lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_UNICAST, 1460));
+ assertEquals(2, lp.getRoutes().size());
+ lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_THROW, 1460));
+ assertEquals(2, lp.getRoutes().size());
+ }
}
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
new file mode 100644
index 0000000..ef15b66
--- /dev/null
+++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.net.wifi.aware.DiscoverySession
+import android.net.wifi.aware.PeerHandle
+import android.net.wifi.aware.WifiAwareNetworkSpecifier
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+
+import com.android.testutils.assertParcelSane
+
+import java.lang.IllegalStateException
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MatchAllNetworkSpecifierTest {
+ @Test
+ fun testParcel() {
+ assertParcelSane(MatchAllNetworkSpecifier(), 0)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testSatisfiedBy() {
+ val specifier = MatchAllNetworkSpecifier()
+ val discoverySession = Mockito.mock(DiscoverySession::class.java)
+ val peerHandle = Mockito.mock(PeerHandle::class.java)
+ val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession,
+ peerHandle).build()
+ specifier.satisfiedBy(wifiAwareNetworkSpecifier)
+ }
+}
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..46f39dd
--- /dev/null
+++ b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.net.NattSocketKeepalive.NATT_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertEqualBothWays
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.parcelingRoundTrip
+import java.net.InetAddress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NattKeepalivePacketDataTest {
+ @Rule @JvmField
+ val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+ /* Refer to the definition in {@code NattKeepalivePacketData} */
+ private val IPV4_HEADER_LENGTH = 20
+ private val UDP_HEADER_LENGTH = 8
+
+ private val TEST_PORT = 4243
+ private val TEST_PORT2 = 4244
+ private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+ private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+ private val TEST_ADDRV6 = "2001:db8::1".address()
+
+ private fun String.address() = InetAddresses.parseNumericAddress(this)
+ private fun nattKeepalivePacket(
+ srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+ srcPort: Int = TEST_PORT,
+ dstAddress: InetAddress? = TEST_DST_ADDRV4,
+ dstPort: Int = NATT_PORT
+ ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort)
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testConstructor() {
+ try {
+ nattKeepalivePacket(dstPort = TEST_PORT)
+ fail("Dst port is not NATT port should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_PORT)
+ }
+
+ try {
+ nattKeepalivePacket(srcAddress = TEST_ADDRV6)
+ fail("A v6 srcAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ nattKeepalivePacket(dstAddress = TEST_ADDRV6)
+ fail("A v6 dstAddress should cause exception")
+ } catch (e: InvalidPacketException) {
+ assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+ }
+
+ try {
+ parcelingRoundTrip(
+ NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT,
+ byteArrayOf(12, 31, 22, 44)))
+ fail("Invalid data should cause exception")
+ } catch (e: IllegalArgumentException) { }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testParcel() {
+ assertParcelSane(nattKeepalivePacket(), 0)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testEquals() {
+ assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket())
+ assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket())
+ // Test src port only because dst port have to be NATT_PORT
+ assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket())
+ // Make sure the parceling test is updated if fields are added in the base class.
+ assertFieldCountEquals(5, KeepalivePacketData::class.java)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testHashCode() {
+ assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode())
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
new file mode 100644
index 0000000..a4d8353
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.assertParcelSane
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkAgentConfigTest {
+ @Rule @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testParcelNetworkAgentConfig() {
+ val config = NetworkAgentConfig.Builder().apply {
+ setExplicitlySelected(true)
+ setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+ setSubscriberId("MySubId")
+ setPartialConnectivityAcceptable(false)
+ setUnvalidatedConnectivityAcceptable(true)
+ }.build()
+ assertParcelSane(config, 10)
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testBuilder() {
+ val config = NetworkAgentConfig.Builder().apply {
+ setExplicitlySelected(true)
+ setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+ setSubscriberId("MySubId")
+ setPartialConnectivityAcceptable(false)
+ setUnvalidatedConnectivityAcceptable(true)
+ setLegacyTypeName("TEST_NETWORK")
+ disableNat64Detection()
+ disableProvisioningNotification()
+ }.build()
+
+ assertTrue(config.isExplicitlySelected())
+ assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType())
+ assertEquals("MySubId", config.getSubscriberId())
+ assertFalse(config.isPartialConnectivityAcceptable())
+ assertTrue(config.isUnvalidatedConnectivityAcceptable())
+ assertEquals("TEST_NETWORK", config.getLegacyTypeName())
+ assertFalse(config.isNat64DetectionEnabled())
+ assertFalse(config.isProvisioningNotificationEnabled())
+ }
+}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6bc7c1b..3f8261d 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,8 @@
package android.net;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
@@ -32,28 +34,45 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.os.Parcel;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.os.Build;
+import android.os.Process;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
+import androidx.core.os.BuildCompat;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import java.util.Arrays;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -62,6 +81,19 @@
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+ @Rule
+ public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
+ private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
+ private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
+
+ private boolean isAtLeastR() {
+ // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
+ // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
+ // releasing Android R.
+ return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+ }
+
@Test
public void testMaybeMarkCapabilitiesRestricted() {
// verify EIMS is restricted
@@ -267,9 +299,36 @@
.setUids(uids)
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
- assertEqualsThroughMarshalling(netCap);
+ if (isAtLeastR()) {
+ netCap.setOwnerUid(123);
+ netCap.setAdministratorUids(new int[] {5, 11});
+ }
+ assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertEqualsThroughMarshalling(netCap);
+ testParcelSane(netCap);
+ }
+
+ @Test
+ public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
+ final NetworkCapabilities netCap = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ if (isAtLeastR()) {
+ netCap.setRequestorPackageName("com.android.test");
+ netCap.setRequestorUid(9304);
+ }
+ assertParcelingIsLossless(netCap);
+ netCap.setSSID(TEST_SSID);
+ testParcelSane(netCap);
+ }
+
+ private void testParcelSane(NetworkCapabilities cap) {
+ if (isAtLeastR()) {
+ assertParcelSane(cap, 15);
+ } else {
+ assertParcelSane(cap, 11);
+ }
}
@Test
@@ -402,6 +461,23 @@
return range;
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testSetAdministratorUids() {
+ NetworkCapabilities nc =
+ new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
+
+ assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testSetAdministratorUidsWithDuplicates() {
+ try {
+ new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
+ fail("Expected IllegalArgumentException for duplicate uids");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
@Test
public void testCombineCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -426,7 +502,9 @@
nc1.setSSID(TEST_SSID);
nc2.combineCapabilities(nc1);
- assertTrue(TEST_SSID.equals(nc2.getSSID()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
// Because they now have the same SSID, the following call should not throw
nc2.combineCapabilities(nc1);
@@ -454,6 +532,31 @@
assertTrue(nc2.appliesToUid(22));
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testCombineCapabilities_AdministratorUids() {
+ final NetworkCapabilities nc1 = new NetworkCapabilities();
+ final NetworkCapabilities nc2 = new NetworkCapabilities();
+
+ final int[] adminUids = {3, 6, 12};
+ nc1.setAdministratorUids(adminUids);
+ nc2.combineCapabilities(nc1);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+ assertArrayEquals(nc2.getAdministratorUids(), adminUids);
+
+ final int[] adminUidsOtherOrder = {3, 12, 6};
+ nc1.setAdministratorUids(adminUidsOtherOrder);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+
+ final int[] adminUids2 = {11, 1, 12, 3, 6};
+ nc1.setAdministratorUids(adminUids2);
+ assertFalse(nc2.equalsAdministratorUids(nc1));
+ assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
+ try {
+ nc2.combineCapabilities(nc1);
+ fail("Shouldn't be able to combine different lists of admin UIDs");
+ } catch (IllegalStateException expected) { }
+ }
+
@Test
public void testSetCapabilities() {
final int[] REQUIRED_CAPABILITIES = new int[] {
@@ -542,18 +645,6 @@
nc1.combineCapabilities(nc3);
}
- private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) {
- Parcel p = Parcel.obtain();
- netCap.writeToParcel(p, /* flags */ 0);
- p.setDataPosition(0);
- byte[] marshalledData = p.marshall();
-
- p = Parcel.obtain();
- p.unmarshall(marshalledData, 0, marshalledData.length);
- p.setDataPosition(0);
- assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap);
- }
-
@Test
public void testSet() {
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -576,12 +667,16 @@
// from nc2.
assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(TEST_SSID.equals(nc2.getSSID()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setSSID(DIFFERENT_TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
- assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSSID()));
+ if (isAtLeastR()) {
+ assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
@@ -603,4 +698,238 @@
assertEquals(TRANSPORT_VPN, transportTypes[2]);
assertEquals(TRANSPORT_TEST, transportTypes[3]);
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testTelephonyNetworkSpecifier() {
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ assertEquals(specifier, nc1.getNetworkSpecifier());
+ try {
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setNetworkSpecifier(specifier)
+ .build();
+ fail("Must have a single transport type. Without transport type or multiple transport"
+ + " types is invalid.");
+ } catch (IllegalStateException expected) { }
+ }
+
+ @Test
+ public void testWifiAwareNetworkSpecifier() {
+ final NetworkCapabilities nc = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI_AWARE);
+ // If NetworkSpecifier is not set, the default value is null.
+ assertNull(nc.getNetworkSpecifier());
+ final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder(
+ mDiscoverySession, mPeerHandle).build();
+ nc.setNetworkSpecifier(specifier);
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testAdministratorUidsAndOwnerUid() {
+ // Test default owner uid.
+ // If the owner uid is not set, the default value should be Process.INVALID_UID.
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
+ assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+ // Test setAdministratorUids and getAdministratorUids.
+ final int[] administratorUids = {1001, 10001};
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .build();
+ assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids()));
+ // Test setOwnerUid and getOwnerUid.
+ // The owner UID must be included in administrator UIDs, or throw IllegalStateException.
+ try {
+ final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+ .setOwnerUid(1001)
+ .build();
+ fail("The owner UID must be included in administrator UIDs.");
+ } catch (IllegalStateException expected) { }
+ final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(1001)
+ .build();
+ assertEquals(1001, nc4.getOwnerUid());
+ try {
+ final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(null)
+ .build();
+ fail("Should not set null into setAdministratorUids");
+ } catch (NullPointerException expected) { }
+ }
+
+ @Test
+ public void testLinkBandwidthKbps() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED.
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps());
+ nc.setLinkDownstreamBandwidthKbps(512);
+ nc.setLinkUpstreamBandwidthKbps(128);
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ }
+
+ @Test
+ public void testSignalStrength() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED.
+ assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength());
+ nc.setSignalStrength(-80);
+ assertEquals(-80, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testDeduceRestrictedCapability() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // Default capabilities don't have restricted capability.
+ assertFalse(nc.deduceRestrictedCapability());
+ // If there is a force restricted capability, then the network capabilities is restricted.
+ nc.addCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, if there is any unrestricted capability in
+ // capabilities, then the network capabilities is not restricted.
+ nc.removeCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_CBS);
+ assertFalse(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, the network capabilities will only be treated
+ // as restricted when there is no any unrestricted capability.
+ nc.removeCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ }
+
+ private void assertNoTransport(NetworkCapabilities nc) {
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+
+ // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all
+ // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence
+ // is true. If positiveSequence is false, then the check sequence is opposite.
+ private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType,
+ boolean positiveSequence) {
+ for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) {
+ if (positiveSequence) {
+ assertTrue(nc.hasTransport(i));
+ } else {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+ for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+ if (positiveSequence) {
+ assertFalse(nc.hasTransport(i));
+ } else {
+ assertTrue(nc.hasTransport(i));
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleTransportTypes() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ assertNoTransport(nc);
+ // Test adding multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.addTransportType(i);
+ checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
+ }
+ // Test removing multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.removeTransportType(i);
+ checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
+ }
+ assertNoTransport(nc);
+ nc.addTransportType(TRANSPORT_WIFI);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ nc.addTransportType(TRANSPORT_VPN);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_WIFI);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_VPN);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ assertNoTransport(nc);
+ }
+
+ @Test
+ public void testAddAndRemoveTransportType() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ try {
+ nc.addTransportType(-1);
+ fail("Should not set invalid transport type into addTransportType");
+ } catch (IllegalArgumentException expected) { }
+ try {
+ nc.removeTransportType(-1);
+ fail("Should not set invalid transport type into removeTransportType");
+ } catch (IllegalArgumentException e) { }
+ }
+
+ private class TestTransportInfo implements TransportInfo {
+ TestTransportInfo() {
+ }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testBuilder() {
+ final int ownerUid = 1001;
+ final int signalStrength = -80;
+ final int requestUid = 10100;
+ final int[] administratorUids = {ownerUid, 10001};
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final TestTransportInfo transportInfo = new TestTransportInfo();
+ final String ssid = "TEST_SSID";
+ final String packageName = "com.google.test.networkcapabilities";
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .removeTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_CBS)
+ .removeCapability(NET_CAPABILITY_CBS)
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(ownerUid)
+ .setLinkDownstreamBandwidthKbps(512)
+ .setLinkUpstreamBandwidthKbps(128)
+ .setNetworkSpecifier(specifier)
+ .setTransportInfo(transportInfo)
+ .setSignalStrength(signalStrength)
+ .setSsid(ssid)
+ .setRequestorUid(requestUid)
+ .setRequestorPackageName(packageName)
+ .build();
+ assertEquals(1, nc.getTransportTypes().length);
+ assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
+ assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_CBS));
+ assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids()));
+ assertEquals(ownerUid, nc.getOwnerUid());
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ assertEquals(transportInfo, nc.getTransportInfo());
+ assertEquals(signalStrength, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ assertEquals(ssid, nc.getSsid());
+ assertEquals(requestUid, nc.getRequestorUid());
+ assertEquals(packageName, nc.getRequestorPackageName());
+ // Cannot assign null into NetworkCapabilities.Builder
+ try {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
+ fail("Should not set null into NetworkCapabilities.Builder");
+ } catch (NullPointerException expected) { }
+ assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+ }
}
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
new file mode 100644
index 0000000..b7c47c2
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 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.app.Instrumentation
+import android.content.Context
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.os.Build
+import android.os.HandlerThread
+import android.os.Looper
+import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn
+import androidx.test.InstrumentationRegistry
+import com.android.testutils.ArrayTrackRecord
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.util.UUID
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val DEFAULT_TIMEOUT_MS = 5000L
+private val instrumentation: Instrumentation
+ get() = InstrumentationRegistry.getInstrumentation()
+private val context: Context get() = InstrumentationRegistry.getContext()
+private val PROVIDER_NAME = "NetworkProviderTest"
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class NetworkProviderTest {
+ private val mCm = context.getSystemService(ConnectivityManager::class.java)
+ private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+
+ @Before
+ fun setUp() {
+ instrumentation.getUiAutomation().adoptShellPermissionIdentity()
+ mHandlerThread.start()
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ instrumentation.getUiAutomation().dropShellPermissionIdentity()
+ }
+
+ private class TestNetworkProvider(context: Context, looper: Looper) :
+ NetworkProvider(context, looper, PROVIDER_NAME) {
+ private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+
+ sealed class CallbackEntry {
+ data class OnNetworkRequested(
+ val request: NetworkRequest,
+ val score: Int,
+ val id: Int
+ ) : CallbackEntry()
+ data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry()
+ }
+
+ override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) {
+ seenEvents.add(OnNetworkRequested(request, score, id))
+ }
+
+ override fun onNetworkRequestWithdrawn(request: NetworkRequest) {
+ seenEvents.add(OnNetworkRequestWithdrawn(request))
+ }
+
+ inline fun <reified T : CallbackEntry> expectCallback(
+ crossinline predicate: (T) -> Boolean
+ ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+ }
+
+ private fun createNetworkProvider(): TestNetworkProvider {
+ return TestNetworkProvider(context, mHandlerThread.looper)
+ }
+
+ @Test
+ fun testOnNetworkRequested() {
+ val provider = createNetworkProvider()
+ assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+ mCm.registerNetworkProvider(provider)
+ assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+
+ val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+ val nr: NetworkRequest = NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(specifier)
+ .build()
+ val cb = ConnectivityManager.NetworkCallback()
+ mCm.requestNetwork(nr, cb)
+ provider.expectCallback<OnNetworkRequested>() { callback ->
+ callback.request.getNetworkSpecifier() == specifier &&
+ callback.request.hasTransport(TRANSPORT_TEST)
+ }
+
+ val initialScore = 40
+ val updatedScore = 60
+ val nc = NetworkCapabilities().apply {
+ addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ setNetworkSpecifier(specifier)
+ }
+ val lp = LinkProperties()
+ val config = NetworkAgentConfig.Builder().build()
+ val agent = object : NetworkAgent(context, mHandlerThread.looper, "TestAgent", nc, lp,
+ initialScore, config, provider) {}
+
+ provider.expectCallback<OnNetworkRequested>() { callback ->
+ callback.request.getNetworkSpecifier() == specifier &&
+ callback.score == initialScore &&
+ callback.id == agent.providerId
+ }
+
+ agent.sendNetworkScore(updatedScore)
+ provider.expectCallback<OnNetworkRequested>() { callback ->
+ callback.request.getNetworkSpecifier() == specifier &&
+ callback.score == updatedScore &&
+ callback.id == agent.providerId
+ }
+
+ mCm.unregisterNetworkCallback(cb)
+ provider.expectCallback<OnNetworkRequestWithdrawn>() { callback ->
+ callback.request.getNetworkSpecifier() == specifier &&
+ callback.request.hasTransport(TRANSPORT_TEST)
+ }
+ mCm.unregisterNetworkProvider(provider)
+ // Provider id should be ID_NONE after unregister network provider
+ assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+ // unregisterNetworkProvider should not crash even if it's called on an
+ // already unregistered provider.
+ mCm.unregisterNetworkProvider(provider)
+ }
+
+ private class TestNetworkCallback : ConnectivityManager.NetworkCallback() {
+ private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+ sealed class CallbackEntry {
+ object OnUnavailable : CallbackEntry()
+ }
+
+ override fun onUnavailable() {
+ seenEvents.add(OnUnavailable)
+ }
+
+ inline fun <reified T : CallbackEntry> expectCallback(
+ crossinline predicate: (T) -> Boolean
+ ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+ }
+
+ @Test
+ fun testDeclareNetworkRequestUnfulfillable() {
+ val provider = createNetworkProvider()
+ mCm.registerNetworkProvider(provider)
+
+ val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+ val nr: NetworkRequest = NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .setNetworkSpecifier(specifier)
+ .build()
+
+ val cb = TestNetworkCallback()
+ mCm.requestNetwork(nr, cb)
+ provider.declareNetworkRequestUnfulfillable(nr)
+ cb.expectCallback<OnUnavailable>() { nr.getNetworkSpecifier() == specifier }
+ mCm.unregisterNetworkProvider(provider)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/tests/net/common/java/android/net/NetworkSpecifierTest.kt
new file mode 100644
index 0000000..f3409f5
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkSpecifierTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertTrue
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class NetworkSpecifierTest {
+ private class TestNetworkSpecifier(
+ val intData: Int = 123,
+ val stringData: String = "init"
+ ) : NetworkSpecifier() {
+ override fun canBeSatisfiedBy(other: NetworkSpecifier?): Boolean =
+ other != null &&
+ other is TestNetworkSpecifier &&
+ other.intData >= intData &&
+ stringData.equals(other.stringData)
+
+ override fun redact(): NetworkSpecifier = TestNetworkSpecifier(intData, "redact")
+ }
+
+ @Test
+ fun testRedact() {
+ val ns: TestNetworkSpecifier = TestNetworkSpecifier()
+ val redactNs = ns.redact()
+ assertTrue(redactNs is TestNetworkSpecifier)
+ assertEquals(ns.intData, redactNs.intData)
+ assertNotEquals(ns.stringData, redactNs.stringData)
+ assertTrue("redact".equals(redactNs.stringData))
+ }
+
+ @Test
+ fun testcanBeSatisfiedBy() {
+ val target: TestNetworkSpecifier = TestNetworkSpecifier()
+ assertFalse(target.canBeSatisfiedBy(null))
+ assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier()))
+ val otherNs = TelephonyNetworkSpecifier.Builder().setSubscriptionId(123).build()
+ assertFalse(target.canBeSatisfiedBy(otherNs))
+ assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 999)))
+ assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 1)))
+ assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(stringData = "diff")))
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
index 38bc744..11d44b8 100644
--- a/tests/net/common/java/android/net/NetworkTest.java
+++ b/tests/net/common/java/android/net/NetworkTest.java
@@ -18,13 +18,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.net.Network;
import android.platform.test.annotations.AppModeFull;
import androidx.test.filters.SmallTest;
@@ -40,7 +37,6 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.SocketException;
-import java.util.Objects;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -123,29 +119,29 @@
Network three = new Network(3);
// None of the hashcodes are zero.
- assertNotEqual(0, one.hashCode());
- assertNotEqual(0, two.hashCode());
- assertNotEqual(0, three.hashCode());
+ assertNotEquals(0, one.hashCode());
+ assertNotEquals(0, two.hashCode());
+ assertNotEquals(0, three.hashCode());
// All the hashcodes are distinct.
- assertNotEqual(one.hashCode(), two.hashCode());
- assertNotEqual(one.hashCode(), three.hashCode());
- assertNotEqual(two.hashCode(), three.hashCode());
+ assertNotEquals(one.hashCode(), two.hashCode());
+ assertNotEquals(one.hashCode(), three.hashCode());
+ assertNotEquals(two.hashCode(), three.hashCode());
// None of the handles are zero.
- assertNotEqual(0, one.getNetworkHandle());
- assertNotEqual(0, two.getNetworkHandle());
- assertNotEqual(0, three.getNetworkHandle());
+ assertNotEquals(0, one.getNetworkHandle());
+ assertNotEquals(0, two.getNetworkHandle());
+ assertNotEquals(0, three.getNetworkHandle());
// All the handles are distinct.
- assertNotEqual(one.getNetworkHandle(), two.getNetworkHandle());
- assertNotEqual(one.getNetworkHandle(), three.getNetworkHandle());
- assertNotEqual(two.getNetworkHandle(), three.getNetworkHandle());
+ assertNotEquals(one.getNetworkHandle(), two.getNetworkHandle());
+ assertNotEquals(one.getNetworkHandle(), three.getNetworkHandle());
+ assertNotEquals(two.getNetworkHandle(), three.getNetworkHandle());
// The handles are not equal to the hashcodes.
- assertNotEqual(one.hashCode(), one.getNetworkHandle());
- assertNotEqual(two.hashCode(), two.getNetworkHandle());
- assertNotEqual(three.hashCode(), three.getNetworkHandle());
+ assertNotEquals(one.hashCode(), one.getNetworkHandle());
+ assertNotEquals(two.hashCode(), two.getNetworkHandle());
+ assertNotEquals(three.hashCode(), three.getNetworkHandle());
// Adjust as necessary to test an implementation's specific constants.
// When running with runtest, "adb logcat -s TestRunner" can be useful.
@@ -154,15 +150,11 @@
assertEquals(16290598925L, three.getNetworkHandle());
}
- private static <T> void assertNotEqual(T t1, T t2) {
- assertFalse(Objects.equals(t1, t2));
- }
-
@Test
public void testGetPrivateDnsBypassingCopy() {
final Network copy = mNetwork.getPrivateDnsBypassingCopy();
assertEquals(mNetwork.netId, copy.netId);
- assertNotEqual(copy.netId, copy.getNetIdForResolv());
- assertNotEqual(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv());
+ assertNotEquals(copy.netId, copy.getNetIdForResolv());
+ assertNotEquals(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv());
}
}
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java
index 2edbd40..60cac0b 100644
--- a/tests/net/common/java/android/net/RouteInfoTest.java
+++ b/tests/net/common/java/android/net/RouteInfoTest.java
@@ -18,34 +18,83 @@
import static android.net.RouteInfo.RTN_UNREACHABLE;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
+import static com.android.testutils.MiscAssertsKt.assertEqualBothWays;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
-import junit.framework.TestCase;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Build;
+
+import androidx.core.os.BuildCompat;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-public class RouteInfoTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RouteInfoTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
+ private static final int INVALID_ROUTE_TYPE = -1;
private InetAddress Address(String addr) {
- return InetAddress.parseNumericAddress(addr);
+ return InetAddresses.parseNumericAddress(addr);
}
private IpPrefix Prefix(String prefix) {
return new IpPrefix(prefix);
}
- @SmallTest
+ private static boolean isAtLeastR() {
+ // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+ }
+
+ @Test
public void testConstructor() {
RouteInfo r;
-
// Invalid input.
try {
r = new RouteInfo((IpPrefix) null, null, "rmnet0");
fail("Expected RuntimeException: destination and gateway null");
- } catch(RuntimeException e) {}
+ } catch (RuntimeException e) { }
+
+ try {
+ r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0",
+ INVALID_ROUTE_TYPE);
+ fail("Invalid route type should cause exception");
+ } catch (IllegalArgumentException e) { }
+
+ try {
+ r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0",
+ RTN_UNREACHABLE);
+ fail("Address family mismatch should cause exception");
+ } catch (IllegalArgumentException e) { }
+
+ try {
+ r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0",
+ RTN_UNREACHABLE);
+ fail("Address family mismatch should cause exception");
+ } catch (IllegalArgumentException e) { }
// Null destination is default route.
r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null);
@@ -70,6 +119,7 @@
assertNull(r.getInterface());
}
+ @Test
public void testMatches() {
class PatchedRouteInfo {
private final RouteInfo mRouteInfo;
@@ -109,49 +159,41 @@
assertFalse(ipv4Default.matches(Address("2001:db8::f00")));
}
- private void assertAreEqual(Object o1, Object o2) {
- assertTrue(o1.equals(o2));
- assertTrue(o2.equals(o1));
- }
-
- private void assertAreNotEqual(Object o1, Object o2) {
- assertFalse(o1.equals(o2));
- assertFalse(o2.equals(o1));
- }
-
+ @Test
public void testEquals() {
// IPv4
RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0");
RouteInfo r2 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0");
- assertAreEqual(r1, r2);
+ assertEqualBothWays(r1, r2);
RouteInfo r3 = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "wlan0");
RouteInfo r4 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::2"), "wlan0");
RouteInfo r5 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "rmnet0");
- assertAreNotEqual(r1, r3);
- assertAreNotEqual(r1, r4);
- assertAreNotEqual(r1, r5);
+ assertNotEqualEitherWay(r1, r3);
+ assertNotEqualEitherWay(r1, r4);
+ assertNotEqualEitherWay(r1, r5);
// IPv6
r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0");
r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0");
- assertAreEqual(r1, r2);
+ assertEqualBothWays(r1, r2);
r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0");
r4 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.2"), "wlan0");
r5 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "rmnet0");
- assertAreNotEqual(r1, r3);
- assertAreNotEqual(r1, r4);
- assertAreNotEqual(r1, r5);
+ assertNotEqualEitherWay(r1, r3);
+ assertNotEqualEitherWay(r1, r4);
+ assertNotEqualEitherWay(r1, r5);
// Interfaces (but not destinations or gateways) can be null.
r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null);
r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null);
r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0");
- assertAreEqual(r1, r2);
- assertAreNotEqual(r1, r3);
+ assertEqualBothWays(r1, r2);
+ assertNotEqualEitherWay(r1, r3);
}
+ @Test
public void testHostAndDefaultRoutes() {
RouteInfo r;
@@ -160,80 +202,133 @@
assertTrue(r.isDefaultRoute());
assertTrue(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0");
assertFalse(r.isHostRoute());
assertTrue(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertTrue(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
assertFalse(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0");
assertFalse(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
assertTrue(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE);
assertFalse(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertTrue(r.isIPv4UnreachableDefault());
+ assertFalse(r.isIPv6UnreachableDefault());
+ }
r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE);
assertFalse(r.isHostRoute());
assertFalse(r.isDefaultRoute());
assertFalse(r.isIPv4Default());
assertFalse(r.isIPv6Default());
+ if (isAtLeastR()) {
+ assertFalse(r.isIPv4UnreachableDefault());
+ assertTrue(r.isIPv6UnreachableDefault());
+ }
}
+ @Test
public void testTruncation() {
LinkAddress l;
RouteInfo r;
@@ -250,6 +345,7 @@
// Make sure that creating routes to multicast addresses doesn't throw an exception. Even though
// there's nothing we can do with them, we don't want to crash if, e.g., someone calls
// requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI);
+ @Test
public void testMulticastRoute() {
RouteInfo r;
r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0");
@@ -257,32 +353,82 @@
// No exceptions? Good.
}
- public RouteInfo passThroughParcel(RouteInfo r) {
- Parcel p = Parcel.obtain();
- RouteInfo r2 = null;
- try {
- r.writeToParcel(p, 0);
- p.setDataPosition(0);
- r2 = RouteInfo.CREATOR.createFromParcel(p);
- } finally {
- p.recycle();
- }
- assertNotNull(r2);
- return r2;
- }
-
- public void assertParcelingIsLossless(RouteInfo r) {
- RouteInfo r2 = passThroughParcel(r);
- assertEquals(r, r2);
- }
-
+ @Test
public void testParceling() {
RouteInfo r;
-
- r = new RouteInfo(Prefix("::/0"), Address("2001:db8::"), null);
+ r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null);
assertParcelingIsLossless(r);
-
r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
assertParcelingIsLossless(r);
+ r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE);
+ assertParcelingIsLossless(r);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testMtuParceling() {
+ final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface",
+ RTN_UNREACHABLE, 1450 /* mtu */);
+ assertParcelingIsLossless(r);
+ }
+
+ @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+ public void testFieldCount_Q() {
+ assertFieldCountEquals(6, RouteInfo.class);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testFieldCount() {
+ // Make sure any new field is covered by the above parceling tests when changing this number
+ assertFieldCountEquals(7, RouteInfo.class);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testMtu() {
+ RouteInfo r;
+ r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0",
+ RouteInfo.RTN_UNICAST, 1500);
+ assertEquals(1500, r.getMtu());
+
+ r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0");
+ assertEquals(0, r.getMtu());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testRouteKey() {
+ RouteInfo.RouteKey k1, k2;
+ // Only prefix, null gateway and null interface
+ k1 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // With prefix, gateway and interface. Type and MTU does not affect RouteKey equality
+ k1 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0",
+ RTN_UNREACHABLE, 1450).getRouteKey();
+ k2 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0",
+ RouteInfo.RTN_UNICAST, 1400).getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // Different scope IDs are ignored by the kernel, so we consider them equal here too.
+ k1 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%1"), "wlan0").getRouteKey();
+ k2 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%2"), "wlan0").getRouteKey();
+ assertEquals(k1, k2);
+ assertEquals(k1.hashCode(), k2.hashCode());
+
+ // Different prefix
+ k1 = new RouteInfo(Prefix("192.0.2.0/24"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("192.0.3.0/24"), null).getRouteKey();
+ assertNotEquals(k1, k2);
+
+ // Different gateway
+ k1 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), null).getRouteKey();
+ k2 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::2"), null).getRouteKey();
+ assertNotEquals(k1, k2);
+
+ // Different interface
+ k1 = new RouteInfo(Prefix("ff02::1/128"), null, "tun0").getRouteKey();
+ k2 = new RouteInfo(Prefix("ff02::1/128"), null, "tun1").getRouteKey();
+ assertNotEquals(k1, k2);
}
}
diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java
index 5096be2..b5f23bf 100644
--- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java
+++ b/tests/net/common/java/android/net/StaticIpConfigurationTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -34,7 +35,6 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -61,10 +61,6 @@
assertEquals(0, s.dnsServers.size());
}
- private static <T> void assertNotEquals(T t1, T t2) {
- assertFalse(Objects.equals(t1, t2));
- }
-
private StaticIpConfiguration makeTestObject() {
StaticIpConfiguration s = new StaticIpConfiguration();
s.ipAddress = ADDR;
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
index 0ce7c91..8480544 100644
--- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -16,23 +16,36 @@
package android.net.apf;
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.ParcelableTestUtil;
-import com.android.internal.util.TestUtils;
-
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ApfCapabilitiesTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
@Test
public void testConstructAndParcel() {
final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
@@ -40,9 +53,7 @@
assertEquals(456, caps.maximumApfProgramSize);
assertEquals(789, caps.apfPacketFormat);
- ParcelableTestUtil.assertFieldCountEquals(3, ApfCapabilities.class);
-
- TestUtils.assertParcelingIsLossless(caps);
+ assertParcelSane(caps, 3);
}
@Test
@@ -62,4 +73,27 @@
caps = new ApfCapabilities(4 /* apfVersionSupported */, 5, 6);
assertTrue(caps.hasDataAccess());
}
+
+ @Test
+ public void testGetApfDrop8023Frames() {
+ // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly
+ // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID.
+ final int resId = mContext.getResources().getIdentifier("config_apfDrop802_3Frames",
+ "bool", "android");
+ final boolean shouldDrop8023Frames = mContext.getResources().getBoolean(resId);
+ final boolean actual = ApfCapabilities.getApfDrop8023Frames();
+ assertEquals(shouldDrop8023Frames, actual);
+ }
+
+ @Test
+ public void testGetApfEtherTypeBlackList() {
+ // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly
+ // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID.
+ final int resId = mContext.getResources().getIdentifier("config_apfEthTypeBlackList",
+ "array", "android");
+ final int[] blacklistedEtherTypeArray = mContext.getResources().getIntArray(resId);
+ final int[] actual = ApfCapabilities.getApfEtherTypeBlackList();
+ assertNotNull(actual);
+ assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual));
+ }
}
diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
index 8d055c9..0b7b740 100644
--- a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt
@@ -16,11 +16,9 @@
package android.net.metrics;
-import android.os.Parcelable
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.ParcelableTestUtil
-import com.android.internal.util.TestUtils
+import com.android.testutils.assertParcelSane
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -30,11 +28,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class ApfProgramEventTest {
- private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
- ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
- TestUtils.assertParcelingIsLossless(obj)
- }
-
private infix fun Int.hasFlag(flag: Int) = (this and (1 shl flag)) != 0
@Test
@@ -55,7 +48,7 @@
assertEquals(5, apfProgramEvent.programLength)
assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags)
- testParcel(apfProgramEvent, 6)
+ assertParcelSane(apfProgramEvent, 6)
}
@Test
diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt
index f8eb40c..46a8c8e 100644
--- a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt
+++ b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt
@@ -16,11 +16,9 @@
package android.net.metrics
-import android.os.Parcelable
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.ParcelableTestUtil
-import com.android.internal.util.TestUtils
+import com.android.testutils.assertParcelSane
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,11 +26,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class ApfStatsTest {
- private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
- ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
- TestUtils.assertParcelingIsLossless(obj)
- }
-
@Test
fun testBuilderAndParcel() {
val apfStats = ApfStats.Builder()
@@ -59,6 +52,6 @@
assertEquals(8, apfStats.programUpdatesAllowingMulticast)
assertEquals(9, apfStats.maxProgramSize)
- testParcel(apfStats, 10)
+ assertParcelSane(apfStats, 10)
}
}
diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
index e9d5e6d..236f72e 100644
--- a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt
@@ -1,10 +1,10 @@
package android.net.metrics
-import android.net.metrics.DhcpErrorEvent.errorCodeWithOption
import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH
+import android.net.metrics.DhcpErrorEvent.errorCodeWithOption
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.TestUtils.parcelingRoundTrip
+import com.android.testutils.parcelingRoundTrip
import java.lang.reflect.Modifier
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
@@ -62,4 +62,4 @@
fun testToString_InvalidErrorCode() {
assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString())
}
-}
\ No newline at end of file
+}
diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
index d76ebf6..55b5e49 100644
--- a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt
@@ -16,11 +16,9 @@
package android.net.metrics
-import android.os.Parcelable
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.ParcelableTestUtil
-import com.android.internal.util.TestUtils
+import com.android.testutils.assertParcelSane
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,18 +26,13 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class IpReachabilityEventTest {
- private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
- ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
- TestUtils.assertParcelingIsLossless(obj)
- }
-
@Test
fun testConstructorAndParcel() {
(IpReachabilityEvent.PROBE..IpReachabilityEvent.PROVISIONING_LOST_ORGANIC).forEach {
val ipReachabilityEvent = IpReachabilityEvent(it)
assertEquals(it, ipReachabilityEvent.eventType)
- testParcel(ipReachabilityEvent, 1)
+ assertParcelSane(ipReachabilityEvent, 1)
}
}
}
diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/tests/net/common/java/android/net/metrics/RaEventTest.kt
index f38d328..d9b7203 100644
--- a/tests/net/common/java/android/net/metrics/RaEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/RaEventTest.kt
@@ -16,11 +16,9 @@
package android.net.metrics
-import android.os.Parcelable
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.ParcelableTestUtil
-import com.android.internal.util.TestUtils
+import com.android.testutils.assertParcelSane
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,11 +28,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class RaEventTest {
- private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
- ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
- TestUtils.assertParcelingIsLossless(obj)
- }
-
@Test
fun testConstructorAndParcel() {
var raEvent = RaEvent.Builder().build()
@@ -74,6 +67,6 @@
assertEquals(5, raEvent.rdnssLifetime)
assertEquals(6, raEvent.dnsslLifetime)
- testParcel(raEvent, 6)
+ assertParcelSane(raEvent, 6)
}
}
diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
index c0cef8f..51c0d41 100644
--- a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
+++ b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt
@@ -16,11 +16,9 @@
package android.net.metrics
-import android.os.Parcelable
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
-import com.android.internal.util.ParcelableTestUtil
-import com.android.internal.util.TestUtils
+import com.android.testutils.assertParcelSane
import java.lang.reflect.Modifier
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -33,11 +31,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class ValidationProbeEventTest {
- private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) {
- ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java)
- TestUtils.assertParcelingIsLossless(obj)
- }
-
private infix fun Int.hasType(type: Int) = (type and this) == type
@Test
@@ -58,7 +51,7 @@
assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION)
assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode)
- testParcel(validationProbeEvent, 3)
+ assertParcelSane(validationProbeEvent, 3)
}
@Test
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
new file mode 100644
index 0000000..7b22e45
--- /dev/null
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 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.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.IFACE_VT
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.SET_FOREGROUND
+import android.net.NetworkStats.TAG_NONE
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsApiTest {
+ @Rule
+ @JvmField
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
+
+ private val testStatsEmpty = NetworkStats(0L, 0)
+
+ // Note that these variables need to be initialized outside of constructor, initialize
+ // here with methods that don't exist in Q devices will result in crash.
+
+ // stats1 and stats2 will have some entries with common keys, which are expected to
+ // be merged if performing add on these 2 stats.
+ private lateinit var testStats1: NetworkStats
+ private lateinit var testStats2: NetworkStats
+
+ // This is a result of adding stats1 and stats2, while the merging of common key items is
+ // subject to test later, this should not be initialized with for a loop to add stats1
+ // and stats2 above.
+ private lateinit var testStats3: NetworkStats
+
+ companion object {
+ private const val TEST_IFACE = "test0"
+ private const val TEST_UID1 = 1001
+ private const val TEST_UID2 = 1002
+ }
+
+ @Before
+ fun setUp() {
+ testStats1 = NetworkStats(0L, 0)
+ // Entries which only appear in set1.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
+ assertEquals(8, testStats1.size())
+
+ testStats2 = NetworkStats(0L, 0)
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+ // Entry which only appears in set2.
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+ assertEquals(5, testStats2.size())
+
+ testStats3 = NetworkStats(0L, 9)
+ // Entries which are unique either in stats1 or stats2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+ // Entries which are common for stats1 and stats2 are being merged.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+ assertEquals(9, testStats3.size())
+ }
+
+ @Test
+ fun testAddEntry() {
+ val expectedEntriesInStats2 = arrayOf(
+ Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
+ Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
+ Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
+ Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
+ Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+ // While testStats* are already initialized with addEntry, verify content added
+ // matches expectation.
+ for (i in expectedEntriesInStats2.indices) {
+ val entry = testStats2.getValues(i, null)
+ assertEquals(expectedEntriesInStats2[i], entry)
+ }
+
+ // Verify entry updated with addEntry.
+ val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
+ assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
+ stats.getValues(3, null))
+ }
+
+ @Test
+ fun testAdd() {
+ var stats = NetworkStats(0L, 0)
+ assertNetworkStatsEquals(testStatsEmpty, stats)
+ stats = stats.add(testStats2)
+ assertNetworkStatsEquals(testStats2, stats)
+ stats = stats.add(testStats1)
+ // EMPTY + STATS2 + STATS1 = STATS3
+ assertNetworkStatsEquals(testStats3, stats)
+ }
+
+ @Test
+ fun testParcelUnparcel() {
+ assertParcelingIsLossless(testStatsEmpty)
+ assertParcelingIsLossless(testStats1)
+ assertParcelingIsLossless(testStats2)
+ assertFieldCountEquals(15, NetworkStats::class.java)
+ }
+
+ @Test
+ fun testDescribeContents() {
+ assertEquals(0, testStatsEmpty.describeContents())
+ assertEquals(0, testStats1.describeContents())
+ assertEquals(0, testStats2.describeContents())
+ assertEquals(0, testStats3.describeContents())
+ }
+
+ @Test
+ fun testSubtract() {
+ // STATS3 - STATS2 = STATS1
+ assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
+ // STATS3 - STATS1 = STATS2
+ assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
+ }
+
+ @Test
+ fun testMethodsDontModifyReceiver() {
+ listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
+ val origStats = it.clone()
+ it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+ it.add(testStats3)
+ it.subtract(testStats1)
+ assertNetworkStatsEquals(origStats, it)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
index 9c7cfb0..aaf97f3 100644
--- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt
+++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.net.util;
+package android.net.util
+import android.os.Build
import android.system.NetlinkSocketAddress
import android.system.Os
import android.system.OsConstants.AF_INET
@@ -26,18 +27,26 @@
import android.system.PacketSocketAddress
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
private const val TEST_INDEX = 123
private const val TEST_PORT = 555
+private const val FF_BYTE = 0xff.toByte()
+
@RunWith(AndroidJUnit4::class)
@SmallTest
class SocketUtilsTest {
+ @Rule @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
@Test
fun testMakeNetlinkSocketAddress() {
val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH)
@@ -50,16 +59,21 @@
}
@Test
- fun testMakePacketSocketAddress() {
+ fun testMakePacketSocketAddress_Q() {
val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX)
assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
- val ff = 0xff.toByte()
- val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX,
- byteArrayOf(ff, ff, ff, ff, ff, ff))
+ val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE })
assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress)
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testMakePacketSocketAddress() {
+ val pkAddress = SocketUtils.makePacketSocketAddress(
+ ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE })
+ assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
+ }
+
@Test
fun testCloseSocket() {
// Expect no exception happening with null object.
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
new file mode 100644
index 0000000..09c0e48
--- /dev/null
+++ b/tests/net/integration/AndroidManifest.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.net.integrationtests">
+
+ <!-- For ConnectivityService registerReceiverAsUser (receiving broadcasts) -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <!-- PermissionMonitor sets network permissions for each user -->
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <!-- ConnectivityService sends notifications to BatteryStats -->
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <!-- Reading network status -->
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+ <uses-permission android:name="android.permission.NETWORK_STACK" />
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <!-- Reading DeviceConfig flags -->
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+
+ <!-- This manifest is merged with the base manifest of the real NetworkStack app.
+ Remove the NetworkStackService from the base (real) manifest, and replace with a test
+ service that responds to the same intent -->
+ <service android:name=".TestNetworkStackService"
+ android:process="com.android.server.net.integrationtests.testnetworkstack">
+ <intent-filter>
+ <action android:name="android.net.INetworkStackConnector.Test"/>
+ </intent-filter>
+ </service>
+ <service android:name=".NetworkStackInstrumentationService"
+ android:process="com.android.server.net.integrationtests.testnetworkstack">
+ <intent-filter>
+ <action android:name=".INetworkStackInstrumentation"/>
+ </intent-filter>
+ </service>
+ <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
+ android:process="com.android.server.net.integrationtests.testnetworkstack"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.net.integrationtests"
+ android:label="Frameworks Net Integration Tests" />
+
+</manifest>
diff --git a/tests/net/integration/res/values/config.xml b/tests/net/integration/res/values/config.xml
new file mode 100644
index 0000000..2c8046f
--- /dev/null
+++ b/tests/net/integration/res/values/config.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!--
+ Override configuration for testing. The below settings use the config_ variants, which are
+ normally used by RROs to override the setting with highest priority. -->
+ <integer name="config_captive_portal_dns_probe_timeout">12500</integer>
+ <string name="config_captive_portal_http_url" translatable="false">http://test.android.com</string>
+ <string name="config_captive_portal_https_url" translatable="false">https://secure.test.android.com</string>
+ <string-array name="config_captive_portal_fallback_urls" translatable="false">
+ <item>http://fallback1.android.com</item>
+ <item>http://fallback2.android.com</item>
+ </string-array>
+ <string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
+ </string-array>
+</resources>
diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/tests/net/integration/src/android/net/TestNetworkStackClient.kt
new file mode 100644
index 0000000..01eb514
--- /dev/null
+++ b/tests/net/integration/src/android/net/TestNetworkStackClient.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.IBinder
+import com.android.server.net.integrationtests.TestNetworkStackService
+import org.mockito.Mockito.any
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+import kotlin.test.fail
+
+const val TEST_ACTION_SUFFIX = ".Test"
+
+class TestNetworkStackClient(context: Context) : NetworkStackClient(TestDependencies(context)) {
+ // TODO: consider switching to TrackRecord for more expressive checks
+ private val lastCallbacks = HashMap<Network, INetworkMonitorCallbacks>()
+
+ private class TestDependencies(private val context: Context) : Dependencies {
+ override fun addToServiceManager(service: IBinder) = Unit
+ override fun checkCallerUid() = Unit
+
+ override fun getConnectivityModuleConnector(): ConnectivityModuleConnector {
+ return ConnectivityModuleConnector { _, _, _, inSystemProcess ->
+ getNetworkStackIntent(inSystemProcess)
+ }.also { it.init(context) }
+ }
+
+ private fun getNetworkStackIntent(inSystemProcess: Boolean): Intent? {
+ // Simulate out-of-system-process config: in-process service not found (null intent)
+ if (inSystemProcess) return null
+ val intent = Intent(INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX)
+ val serviceName = TestNetworkStackService::class.qualifiedName
+ ?: fail("TestNetworkStackService name not found")
+ intent.component = ComponentName(context.packageName, serviceName)
+ return intent
+ }
+ }
+
+ // base may be an instance of an inaccessible subclass, so non-spyable.
+ // Use a known open class that delegates to the original instance for all methods except
+ // asBinder. asBinder needs to use its own non-delegated implementation as otherwise it would
+ // return a binder token to a class that is not spied on.
+ open class NetworkMonitorCallbacksWrapper(private val base: INetworkMonitorCallbacks) :
+ INetworkMonitorCallbacks.Stub(), INetworkMonitorCallbacks by base {
+ // asBinder is implemented by both base class and delegate: specify explicitly
+ override fun asBinder(): IBinder {
+ return super.asBinder()
+ }
+ }
+
+ override fun makeNetworkMonitor(network: Network, name: String?, cb: INetworkMonitorCallbacks) {
+ val cbSpy = spy(NetworkMonitorCallbacksWrapper(cb))
+ lastCallbacks[network] = cbSpy
+ super.makeNetworkMonitor(network, name, cbSpy)
+ }
+
+ fun verifyNetworkMonitorCreated(network: Network, timeoutMs: Long) {
+ val cb = lastCallbacks[network]
+ ?: fail("NetworkMonitor for network $network not requested")
+ verify(cb, timeout(timeoutMs)).onNetworkMonitorCreated(any())
+ }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
new file mode 100644
index 0000000..c4801aa
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -0,0 +1,207 @@
+/*
+ * 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 com.android.server.net.integrationtests
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Context.BIND_IMPORTANT
+import android.content.Intent
+import android.content.ServiceConnection
+import android.net.ConnectivityManager
+import android.net.IDnsResolver
+import android.net.INetd
+import android.net.INetworkPolicyManager
+import android.net.INetworkStatsService
+import android.net.LinkProperties
+import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkRequest
+import android.net.TestNetworkStackClient
+import android.net.metrics.IpConnectivityLog
+import android.os.ConditionVariable
+import android.os.IBinder
+import android.os.INetworkManagementService
+import android.testing.TestableContext
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.ConnectivityService
+import com.android.server.LocalServices
+import com.android.server.NetworkAgentWrapper
+import com.android.server.TestNetIdManager
+import com.android.server.connectivity.DefaultNetworkMetrics
+import com.android.server.connectivity.IpConnectivityMetrics
+import com.android.server.connectivity.MockableSystemProperties
+import com.android.server.connectivity.ProxyTracker
+import com.android.server.net.NetworkPolicyManagerInternal
+import com.android.testutils.TestableNetworkCallback
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.MockitoAnnotations
+import org.mockito.Spy
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+const val SERVICE_BIND_TIMEOUT_MS = 5_000L
+const val TEST_TIMEOUT_MS = 1_000L
+
+/**
+ * Test that exercises an instrumented version of ConnectivityService against an instrumented
+ * NetworkStack in a different test process.
+ */
+@RunWith(AndroidJUnit4::class)
+class ConnectivityServiceIntegrationTest {
+ // lateinit used here for mocks as they need to be reinitialized between each test and the test
+ // should crash if they are used before being initialized.
+ @Mock
+ private lateinit var netManager: INetworkManagementService
+ @Mock
+ private lateinit var statsService: INetworkStatsService
+ @Mock
+ private lateinit var policyManager: INetworkPolicyManager
+ @Mock
+ private lateinit var log: IpConnectivityLog
+ @Mock
+ private lateinit var netd: INetd
+ @Mock
+ private lateinit var dnsResolver: IDnsResolver
+ @Mock
+ private lateinit var metricsLogger: IpConnectivityMetrics.Logger
+ @Mock
+ private lateinit var defaultMetrics: DefaultNetworkMetrics
+ @Spy
+ private var context = TestableContext(realContext)
+
+ // lateinit for these three classes under test, as they should be reset to a different instance
+ // for every test but should always be initialized before use (or the test should crash).
+ private lateinit var networkStackClient: TestNetworkStackClient
+ private lateinit var service: ConnectivityService
+ private lateinit var cm: ConnectivityManager
+
+ companion object {
+ // lateinit for this binder token, as it must be initialized before any test code is run
+ // and use of it before init should crash the test.
+ private lateinit var nsInstrumentation: INetworkStackInstrumentation
+ private val bindingCondition = ConditionVariable(false)
+
+ private val realContext get() = InstrumentationRegistry.getInstrumentation().context
+
+ private class InstrumentationServiceConnection : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+ Log.i("TestNetworkStack", "Service connected")
+ try {
+ if (service == null) fail("Error binding to NetworkStack instrumentation")
+ if (::nsInstrumentation.isInitialized) fail("Service already connected")
+ nsInstrumentation = INetworkStackInstrumentation.Stub.asInterface(service)
+ } finally {
+ bindingCondition.open()
+ }
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) = Unit
+ }
+
+ @BeforeClass
+ @JvmStatic
+ fun setUpClass() {
+ val intent = Intent(realContext, NetworkStackInstrumentationService::class.java)
+ intent.action = INetworkStackInstrumentation::class.qualifiedName
+ assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(),
+ BIND_AUTO_CREATE or BIND_IMPORTANT),
+ "Error binding to instrumentation service")
+ assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS),
+ "Timed out binding to instrumentation service " +
+ "after $SERVICE_BIND_TIMEOUT_MS ms")
+ }
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ doReturn(defaultMetrics).`when`(metricsLogger).defaultNetworkMetrics()
+ doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+
+ networkStackClient = TestNetworkStackClient(realContext)
+ networkStackClient.init()
+ networkStackClient.start()
+
+ LocalServices.removeServiceForTest(NetworkPolicyManagerInternal::class.java)
+ LocalServices.addService(NetworkPolicyManagerInternal::class.java,
+ mock(NetworkPolicyManagerInternal::class.java))
+
+ service = TestConnectivityService(makeDependencies())
+ cm = ConnectivityManager(context, service)
+ context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
+
+ service.systemReady()
+ }
+
+ private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
+ context, netManager, statsService, policyManager, dnsResolver, log, netd, deps)
+
+ private fun makeDependencies(): ConnectivityService.Dependencies {
+ val deps = spy(ConnectivityService.Dependencies())
+ doReturn(networkStackClient).`when`(deps).networkStack
+ doReturn(metricsLogger).`when`(deps).metricsLogger
+ doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
+ doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
+ doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
+ return deps
+ }
+
+ @After
+ fun tearDown() {
+ nsInstrumentation.clearAllState()
+ }
+
+ @Test
+ fun testValidation() {
+ val request = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build()
+ val testCallback = TestableNetworkCallback()
+
+ cm.registerNetworkCallback(request, testCallback)
+ nsInstrumentation.addHttpResponse(HttpResponse(
+ "http://test.android.com",
+ responseCode = 204, contentLength = 42, redirectUrl = null))
+ nsInstrumentation.addHttpResponse(HttpResponse(
+ "https://secure.test.android.com",
+ responseCode = 204, contentLength = 42, redirectUrl = null))
+
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+ networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
+
+ na.addCapability(NET_CAPABILITY_INTERNET)
+ na.connect()
+
+ testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
+ assertEquals(2, nsInstrumentation.getRequestUrls().size)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
new file mode 100644
index 0000000..45073d8
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.server.net.integrationtests
+
+import android.os.Parcel
+import android.os.Parcelable
+
+data class HttpResponse(
+ val requestUrl: String,
+ val responseCode: Int,
+ val contentLength: Long,
+ val redirectUrl: String?
+) : Parcelable {
+ constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
+
+ override fun writeToParcel(dest: Parcel, flags: Int) {
+ with(dest) {
+ writeString(requestUrl)
+ writeInt(responseCode)
+ writeLong(contentLength)
+ writeString(redirectUrl)
+ }
+ }
+
+ override fun describeContents() = 0
+ companion object CREATOR : Parcelable.Creator<HttpResponse> {
+ override fun createFromParcel(source: Parcel) = HttpResponse(source)
+ override fun newArray(size: Int) = arrayOfNulls<HttpResponse?>(size)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
new file mode 100644
index 0000000..4827d29
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -0,0 +1,81 @@
+/*
+ * 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 com.android.server.net.integrationtests
+
+import android.app.Service
+import android.content.Intent
+import java.net.URL
+import java.util.Collections
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentLinkedQueue
+import kotlin.collections.ArrayList
+import kotlin.test.fail
+
+/**
+ * An instrumentation interface for the NetworkStack that allows controlling behavior to
+ * facilitate integration tests.
+ */
+class NetworkStackInstrumentationService : Service() {
+ override fun onBind(intent: Intent) = InstrumentationConnector.asBinder()
+
+ object InstrumentationConnector : INetworkStackInstrumentation.Stub() {
+ private val httpResponses = ConcurrentHashMap<String, ConcurrentLinkedQueue<HttpResponse>>()
+ .run {
+ withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } }
+ }
+ private val httpRequestUrls = Collections.synchronizedList(ArrayList<String>())
+
+ /**
+ * Called when an HTTP request is being processed by NetworkMonitor. Returns the response
+ * that should be simulated.
+ */
+ fun processRequest(url: URL): HttpResponse {
+ val strUrl = url.toString()
+ httpRequestUrls.add(strUrl)
+ return httpResponses[strUrl]?.poll()
+ ?: fail("No mocked response for request: $strUrl. " +
+ "Mocked URL keys are: ${httpResponses.keys}")
+ }
+
+ /**
+ * Clear all state of this connector. This is intended for use between two tests, so all
+ * state should be reset as if the connector was just created.
+ */
+ override fun clearAllState() {
+ httpResponses.clear()
+ httpRequestUrls.clear()
+ }
+
+ /**
+ * Add a response to a future HTTP request.
+ *
+ * <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
+ * used to mock the query response.
+ */
+ override fun addHttpResponse(response: HttpResponse) {
+ httpResponses.getValue(response.requestUrl).add(response)
+ }
+
+ /**
+ * Get the ordered list of request URLs that have been sent by NetworkMonitor, and were
+ * answered based on mock responses.
+ */
+ override fun getRequestUrls(): List<String> {
+ return ArrayList(httpRequestUrls)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
new file mode 100644
index 0000000..8c2de40
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -0,0 +1,97 @@
+/*
+ * 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 com.android.server.net.integrationtests
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.net.INetworkMonitorCallbacks
+import android.net.Network
+import android.net.metrics.IpConnectivityLog
+import android.net.util.SharedLog
+import android.os.IBinder
+import com.android.networkstack.netlink.TcpSocketTracker
+import com.android.server.NetworkStackService
+import com.android.server.NetworkStackService.NetworkMonitorConnector
+import com.android.server.NetworkStackService.NetworkStackConnector
+import com.android.server.connectivity.NetworkMonitor
+import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+
+private const val TEST_NETID = 42
+
+/**
+ * Android service that can return an [android.net.INetworkStackConnector] which can be instrumented
+ * through [NetworkStackInstrumentationService].
+ * Useful in tests to create test instrumented NetworkStack components that can receive
+ * instrumentation commands through [NetworkStackInstrumentationService].
+ */
+class TestNetworkStackService : Service() {
+ override fun onBind(intent: Intent): IBinder = TestNetworkStackConnector(makeTestContext())
+
+ private fun makeTestContext() = spy(applicationContext).also {
+ doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE)
+ }
+
+ private class TestPermissionChecker : NetworkStackService.PermissionChecker() {
+ override fun enforceNetworkStackCallingPermission() = Unit
+ }
+
+ private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) :
+ NetworkMonitor.Dependencies() {
+ override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
+ override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
+ }
+
+ private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector(
+ context, TestPermissionChecker(), NetworkStackService.Dependencies()) {
+
+ private val network = Network(TEST_NETID)
+ private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
+
+ private inner class TestNetwork(netId: Int) : Network(netId) {
+ override fun openConnection(url: URL): URLConnection {
+ val response = InstrumentationConnector.processRequest(url)
+
+ val connection = mock(HttpURLConnection::class.java)
+ doReturn(response.responseCode).`when`(connection).responseCode
+ doReturn(response.contentLength).`when`(connection).contentLengthLong
+ doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
+ return connection
+ }
+ }
+
+ override fun makeNetworkMonitor(
+ network: Network,
+ name: String?,
+ cb: INetworkMonitorCallbacks
+ ) {
+ val nm = NetworkMonitor(this@TestNetworkStackService, cb,
+ this.network,
+ mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
+ mock(NetworkStackService.NetworkStackServiceManager::class.java),
+ NetworkMonitorDeps(privateDnsBypassNetwork),
+ mock(TcpSocketTracker::class.java))
+ cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker()))
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
new file mode 100644
index 0000000..fa2b99c
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.server
+
+import android.net.ConnectivityManager.TYPE_BLUETOOTH
+import android.net.ConnectivityManager.TYPE_ETHERNET
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_NONE
+import android.net.ConnectivityManager.TYPE_TEST
+import android.net.ConnectivityManager.TYPE_VPN
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+
+fun transportToLegacyType(transport: Int) = when (transport) {
+ TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH
+ TRANSPORT_CELLULAR -> TYPE_MOBILE
+ TRANSPORT_ETHERNET -> TYPE_ETHERNET
+ TRANSPORT_TEST -> TYPE_TEST
+ TRANSPORT_VPN -> TYPE_VPN
+ TRANSPORT_WIFI -> TYPE_WIFI
+ else -> TYPE_NONE
+}
\ No newline at end of file
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
new file mode 100644
index 0000000..0ffafd4
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -0,0 +1,270 @@
+/*
+ * 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 com.android.server;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkProvider;
+import android.net.NetworkSpecifier;
+import android.net.SocketKeepalive;
+import android.net.UidRange;
+import android.os.ConditionVariable;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.server.connectivity.ConnectivityConstants;
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkCallback;
+
+import java.util.Set;
+
+public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
+ private final NetworkInfo mNetworkInfo;
+ private final NetworkCapabilities mNetworkCapabilities;
+ private final HandlerThread mHandlerThread;
+ private final Context mContext;
+ private final String mLogTag;
+
+ private final ConditionVariable mDisconnected = new ConditionVariable();
+ private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
+ private int mScore;
+ private NetworkAgent mNetworkAgent;
+ private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
+ private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+ private Integer mExpectedKeepaliveSlot = null;
+
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
+ throws Exception {
+ final int type = transportToLegacyType(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ mNetworkCapabilities.addTransportType(transport);
+ switch (transport) {
+ case TRANSPORT_ETHERNET:
+ mScore = 70;
+ break;
+ case TRANSPORT_WIFI:
+ mScore = 60;
+ break;
+ case TRANSPORT_CELLULAR:
+ mScore = 50;
+ break;
+ case TRANSPORT_WIFI_AWARE:
+ mScore = 20;
+ break;
+ case TRANSPORT_VPN:
+ mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
+ // VPNs deduce the SUSPENDED capability from their underlying networks and there
+ // is no public API to let VPN services set it.
+ mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+ break;
+ default:
+ throw new UnsupportedOperationException("unimplemented network type");
+ }
+ mContext = context;
+ mLogTag = "Mock-" + typeName;
+ mHandlerThread = new HandlerThread(mLogTag);
+ mHandlerThread.start();
+
+ mNetworkAgent = makeNetworkAgent(linkProperties);
+ }
+
+ protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+ throws Exception {
+ return new InstrumentedNetworkAgent(this, linkProperties);
+ }
+
+ public static class InstrumentedNetworkAgent extends NetworkAgent {
+ private final NetworkAgentWrapper mWrapper;
+
+ public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
+ super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
+ wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
+ new NetworkAgentConfig(), NetworkProvider.ID_NONE);
+ mWrapper = wrapper;
+ }
+
+ @Override
+ public void unwanted() {
+ mWrapper.mDisconnected.open();
+ }
+
+ @Override
+ public void startSocketKeepalive(Message msg) {
+ int slot = msg.arg1;
+ if (mWrapper.mExpectedKeepaliveSlot != null) {
+ assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
+ }
+ onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+ }
+
+ @Override
+ public void stopSocketKeepalive(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+ }
+
+ @Override
+ protected void preventAutomaticReconnect() {
+ mWrapper.mPreventReconnectReceived.open();
+ }
+
+ @Override
+ protected void addKeepalivePacketFilter(Message msg) {
+ Log.i(mWrapper.mLogTag, "Add keepalive packet filter.");
+ }
+
+ @Override
+ protected void removeKeepalivePacketFilter(Message msg) {
+ Log.i(mWrapper.mLogTag, "Remove keepalive packet filter.");
+ }
+ }
+
+ public void adjustScore(int change) {
+ mScore += change;
+ mNetworkAgent.sendNetworkScore(mScore);
+ }
+
+ public int getScore() {
+ return mScore;
+ }
+
+ public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) {
+ mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated);
+ }
+
+ public void addCapability(int capability) {
+ mNetworkCapabilities.addCapability(capability);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void removeCapability(int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setSignalStrength(int signalStrength) {
+ mNetworkCapabilities.setSignalStrength(signalStrength);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) {
+ mNetworkCapabilities.set(nc);
+ if (sendToConnectivityService) {
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+ }
+
+ public void connect() {
+ assertNotEquals("MockNetworkAgents can only be connected once",
+ getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public void suspend() {
+ removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
+ public void resume() {
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
+ public void disconnect() {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ @Override
+ public Network getNetwork() {
+ return mNetworkAgent.getNetwork();
+ }
+
+ public void expectPreventReconnectReceived(long timeoutMs) {
+ assertTrue(mPreventReconnectReceived.block(timeoutMs));
+ }
+
+ public void expectDisconnected(long timeoutMs) {
+ assertTrue(mDisconnected.block(timeoutMs));
+ }
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mNetworkAgent.sendLinkProperties(lp);
+ }
+
+ public void setStartKeepaliveEvent(int reason) {
+ mStartKeepaliveError = reason;
+ }
+
+ public void setStopKeepaliveEvent(int reason) {
+ mStopKeepaliveError = reason;
+ }
+
+ public void setExpectedKeepaliveSlot(Integer slot) {
+ mExpectedKeepaliveSlot = slot;
+ }
+
+ public NetworkAgent getNetworkAgent() {
+ return mNetworkAgent;
+ }
+
+ public NetworkInfo getNetworkInfo() {
+ return mNetworkInfo;
+ }
+
+ public NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ public void waitForIdle(long timeoutMs) {
+ HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs);
+ }
+}
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
new file mode 100644
index 0000000..eb290dc
--- /dev/null
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -0,0 +1,38 @@
+/*
+ * 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 com.android.server
+
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather
+ * than starting from [NetIdManager.MIN_NET_ID] and increasing.
+ *
+ * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs
+ * overlapping with netIDs used by the real ConnectivityService on the device.
+ *
+ * IDs may still overlap if many networks have been used on the device (so the "real" netIDs
+ * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there
+ * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as
+ * the real ConnectivityService could start using netIds that have been used by the test in the
+ * past.
+ */
+class TestNetIdManager : NetIdManager() {
+ private val nextId = AtomicInteger(MAX_NET_ID)
+ override fun reserveNetId() = nextId.decrementAndGet()
+ override fun releaseNetId(id: Int) = Unit
+}
diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
index fd555c1..899295a 100644
--- a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
@@ -202,8 +202,7 @@
assertFalse(stats.hasNextBucket());
}
- private void assertBucketMatches(Entry expected,
- NetworkStats.Bucket actual) {
+ private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) {
assertEquals(expected.uid, actual.getUid());
assertEquals(expected.rxBytes, actual.getRxBytes());
assertEquals(expected.rxPackets, actual.getRxPackets());
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..1d6c107
--- /dev/null
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2020 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 static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsBinder;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+
+import java.util.concurrent.Executor;
+
+@RunWith(JUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+ private static final int NET_ID = 1;
+ private static final int DETECTION_METHOD = 2;
+ private static final long TIMESTAMP = 10L;
+ private static final String INTERFACE_NAME = "interface";
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+
+ private static final Executor INLINE_EXECUTOR = x -> x.run();
+
+ @Mock private IConnectivityManager mService;
+ @Mock private ConnectivityDiagnosticsCallback mCb;
+
+ private Context mContext;
+ private ConnectivityDiagnosticsBinder mBinder;
+ private ConnectivityDiagnosticsManager mManager;
+
+ private String mPackageName;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+
+ mService = mock(IConnectivityManager.class);
+ mCb = mock(ConnectivityDiagnosticsCallback.class);
+
+ mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
+ mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+
+ mPackageName = mContext.getOpPackageName();
+ }
+
+ @After
+ public void tearDown() {
+ // clear ConnectivityDiagnosticsManager callbacks map
+ ConnectivityDiagnosticsManager.sCallbacks.clear();
+ }
+
+ private ConnectivityReport createSampleConnectivityReport() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ return new ConnectivityReport(
+ new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle);
+ }
+
+ private ConnectivityReport createDefaultConnectivityReport() {
+ return new ConnectivityReport(
+ new Network(0),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testPersistableBundleEquals() {
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ null, PersistableBundle.EMPTY));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, null));
+ assertTrue(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, PersistableBundle.EMPTY));
+
+ final PersistableBundle a = new PersistableBundle();
+ a.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle b = new PersistableBundle();
+ b.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle c = new PersistableBundle();
+ c.putString(BUNDLE_KEY, null);
+
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY));
+
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b));
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a));
+
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c));
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a));
+ }
+
+ @Test
+ public void testConnectivityReportEquals() {
+ final ConnectivityReport defaultReport = createDefaultConnectivityReport();
+ final ConnectivityReport sampleReport = createSampleConnectivityReport();
+ assertEquals(sampleReport, createSampleConnectivityReport());
+ assertEquals(defaultReport, createDefaultConnectivityReport());
+
+ final LinkProperties linkProperties = sampleReport.getLinkProperties();
+ final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+ final PersistableBundle bundle = sampleReport.getAdditionalInfo();
+
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(NET_ID),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ 0L,
+ linkProperties,
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ networkCapabilities,
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ bundle));
+ }
+
+ @Test
+ public void testConnectivityReportParcelUnparcel() {
+ assertParcelSane(createSampleConnectivityReport(), 5);
+ }
+
+ private DataStallReport createSampleDataStallReport() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ return new DataStallReport(
+ new Network(NET_ID),
+ TIMESTAMP,
+ DETECTION_METHOD,
+ linkProperties,
+ networkCapabilities,
+ bundle);
+ }
+
+ private DataStallReport createDefaultDataStallReport() {
+ return new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testDataStallReportEquals() {
+ final DataStallReport defaultReport = createDefaultDataStallReport();
+ final DataStallReport sampleReport = createSampleDataStallReport();
+ assertEquals(sampleReport, createSampleDataStallReport());
+ assertEquals(defaultReport, createDefaultDataStallReport());
+
+ final LinkProperties linkProperties = sampleReport.getLinkProperties();
+ final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+ final PersistableBundle bundle = sampleReport.getStallDetails();
+
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(NET_ID),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ TIMESTAMP,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ DETECTION_METHOD,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ linkProperties,
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ networkCapabilities,
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ bundle));
+ }
+
+ @Test
+ public void testDataStallReportParcelUnparcel() {
+ assertParcelSane(createSampleDataStallReport(), 6);
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() {
+ mBinder.onConnectivityReportAvailable(createSampleConnectivityReport());
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport()));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() {
+ mBinder.onDataStallSuspected(createSampleDataStallReport());
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport()));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() {
+ final Network n = new Network(NET_ID);
+ final boolean connectivity = true;
+
+ mBinder.onNetworkConnectivityReported(n, connectivity);
+
+ // The callback will be invoked synchronously by inline executor. Immediately check the
+ // latch without waiting.
+ verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity));
+ }
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ verify(mService).registerConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
+ assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ try {
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+ fail("Duplicate callback registration should fail");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testUnregisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+ verify(mService).unregisterConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class));
+ assertFalse(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+
+ // verify that re-registering is successful
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+ verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
+ assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+ }
+
+ @Test
+ public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception {
+ mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+ verifyNoMoreInteractions(mService);
+ }
+}
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 7ede144..d6bf334 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -212,7 +212,8 @@
ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
// register callback
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(request);
manager.requestNetwork(request, callback, handler);
@@ -240,7 +241,8 @@
ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
// register callback
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(req1);
manager.requestNetwork(req1, callback, handler);
@@ -258,7 +260,8 @@
verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
// callback can be registered again
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(req2);
manager.requestNetwork(req2, callback, handler);
@@ -282,7 +285,8 @@
info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
when(mCtx.getApplicationInfo()).thenReturn(info);
- when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request);
+ when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any()))
+ .thenReturn(request);
Handler handler = new Handler(Looper.getMainLooper());
manager.requestNetwork(request, callback, handler);
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 0000000..ada5494
--- /dev/null
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,437 @@
+/*
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.net.VpnProfile;
+import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Ikev2VpnProfileTest {
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final String USERNAME_STRING = "username";
+ private static final String PASSWORD_STRING = "pa55w0rd";
+ private static final String EXCL_LIST = "exclList";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+ private static final int TEST_MTU = 1300;
+
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+ private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST);
+
+ private X509Certificate mUserCert;
+ private X509Certificate mServerRootCa;
+ private PrivateKey mPrivateKey;
+
+ @Before
+ public void setUp() throws Exception {
+ mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+ final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+ mUserCert = userCertKey.cert;
+ mPrivateKey = userCertKey.key;
+ }
+
+ private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+ builder.setBypassable(true);
+ builder.setProxy(mProxy);
+ builder.setMaxMtu(TEST_MTU);
+ builder.setMetered(true);
+
+ return builder;
+ }
+
+ @Test
+ public void testBuildValidProfileWithOptions() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ // Check non-auth parameters correctly stored
+ assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+ assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+ assertEquals(mProxy, profile.getProxyInfo());
+ assertTrue(profile.isBypassable());
+ assertTrue(profile.isMetered());
+ assertEquals(TEST_MTU, profile.getMaxMtu());
+ assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testBuildUsernamePasswordProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(USERNAME_STRING, profile.getUsername());
+ assertEquals(PASSWORD_STRING, profile.getPassword());
+ assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildDigitalSignatureProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(profile.getUserCert(), mUserCert);
+ assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+ assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ }
+
+ @Test
+ public void testBuildPresharedKeyProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+ assertNull(profile.getServerRootCaCert());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildWithAllowedAlgorithmsAead() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+
+ List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
+ builder.setAllowedAlgorithms(allowedAlgorithms);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testBuildWithAllowedAlgorithmsNormal() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthPsk(PSK_BYTES);
+
+ List<String> allowedAlgorithms =
+ Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC);
+ builder.setAllowedAlgorithms(allowedAlgorithms);
+
+ final Ikev2VpnProfile profile = builder.build();
+ assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsEmptyList() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setAllowedAlgorithms(new ArrayList<>());
+ fail("Expected exception due to no valid algorithm set");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsInvalidList() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ List<String> allowedAlgorithms = new ArrayList<>();
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256));
+ fail("Expected exception due to missing encryption");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC));
+ fail("Expected exception due to missing authentication");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ List<String> allowedAlgorithms = new ArrayList<>();
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5));
+ fail("Expected exception due to insecure algorithm");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1));
+ fail("Expected exception due to insecure algorithm");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildNoAuthMethodSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.build();
+ fail("Expected exception due to lack of auth method");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildInvalidMtu() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setMaxMtu(500);
+ fail("Expected exception due to too-small MTU");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void verifyVpnProfileCommon(VpnProfile profile) {
+ assertEquals(SERVER_ADDR_STRING, profile.server);
+ assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+ assertEquals(mProxy, profile.proxy);
+ assertTrue(profile.isBypassable);
+ assertTrue(profile.isMetered);
+ assertEquals(TEST_MTU, profile.maxMtu);
+ }
+
+ @Test
+ public void testPskConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecCaCert);
+ }
+
+ @Test
+ public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(USERNAME_STRING, profile.username);
+ assertEquals(PASSWORD_STRING, profile.password);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecSecret);
+ }
+
+ @Test
+ public void testRsaConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+ + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+ assertEquals(
+ expectedSecret,
+ profile.ipsecSecret);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ }
+
+ @Test
+ public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+ profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ assertNull(result.getServerRootCaCert());
+ }
+
+ @Test
+ public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.ipsecSecret = new String(PSK_BYTES);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getPresharedKey());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ }
+
+ @Test
+ public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getPresharedKey());
+ }
+
+ @Test
+ public void testPskConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testUsernamePasswordConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testRsaConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ private static class CertificateAndKey {
+ public final X509Certificate cert;
+ public final PrivateKey key;
+
+ CertificateAndKey(X509Certificate cert, PrivateKey key) {
+ this.cert = cert;
+ this.key = key;
+ }
+ }
+
+ private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+ final Date validityBeginDate =
+ new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+ final Date validityEndDate =
+ new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+ // Generate a keypair
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(512);
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ final X500Principal dnName = new X500Principal("CN=test.android.com");
+ final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+ certGen.setSubjectDN(dnName);
+ certGen.setIssuerDN(dnName);
+ certGen.setNotBefore(validityBeginDate);
+ certGen.setNotAfter(validityEndDate);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+ return new CertificateAndKey(cert, keyPair.getPrivate());
+ }
+}
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index b81ca36..0b13800 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -35,6 +35,7 @@
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.net.ipmemorystore.Status;
+import android.net.networkstack.ModuleNetworkStackClient;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -67,7 +68,7 @@
@Mock
Context mMockContext;
@Mock
- NetworkStackClient mNetworkStackClient;
+ ModuleNetworkStackClient mModuleNetworkStackClient;
@Mock
IIpMemoryStore mMockService;
@Mock
@@ -90,21 +91,21 @@
((IIpMemoryStoreCallbacks) invocation.getArgument(0))
.onIpMemoryStoreFetched(mMockService);
return null;
- }).when(mNetworkStackClient).fetchIpMemoryStore(any());
+ }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any());
} else {
- doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
+ doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
}
mStore = new IpMemoryStore(mMockContext) {
@Override
- protected NetworkStackClient getNetworkStackClient() {
- return mNetworkStackClient;
+ protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) {
+ return mModuleNetworkStackClient;
}
};
}
private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) {
return new NetworkAttributes.Builder()
- .setGroupHint(hint)
+ .setCluster(hint)
.setMtu(mtu)
.build();
}
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index 215506c..c9888b2 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -16,12 +16,12 @@
package android.net;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
-import android.os.Parcel;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import androidx.test.filters.SmallTest;
@@ -89,23 +89,15 @@
IpSecConfig original = getSampleConfig();
IpSecConfig copy = new IpSecConfig(original);
- assertTrue(IpSecConfig.equals(original, copy));
- assertFalse(original == copy);
+ assertEquals(original, copy);
+ assertNotSame(original, copy);
}
@Test
- public void testParcelUnparcel() throws Exception {
+ public void testParcelUnparcel() {
assertParcelingIsLossless(new IpSecConfig());
IpSecConfig c = getSampleConfig();
- assertParcelingIsLossless(c);
- }
-
- private void assertParcelingIsLossless(IpSecConfig ci) throws Exception {
- Parcel p = Parcel.obtain();
- ci.writeToParcel(p, 0);
- p.setDataPosition(0);
- IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p);
- assertTrue(IpSecConfig.equals(co, ci));
+ assertParcelSane(c, 15);
}
}
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java
index 2308a3c..424f23d 100644
--- a/tests/net/java/android/net/IpSecTransformTest.java
+++ b/tests/net/java/android/net/IpSecTransformTest.java
@@ -16,8 +16,8 @@
package android.net;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import androidx.test.filters.SmallTest;
@@ -43,7 +43,7 @@
config.setSpiResourceId(1985);
IpSecTransform postModification = new IpSecTransform(null, config);
- assertFalse(IpSecTransform.equals(preModification, postModification));
+ assertNotEquals(preModification, postModification);
}
@Test
@@ -57,6 +57,6 @@
IpSecTransform config1 = new IpSecTransform(null, config);
IpSecTransform config2 = new IpSecTransform(null, config);
- assertTrue(IpSecTransform.equals(config1, config2));
+ assertEquals(config1, config2);
}
}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index daf187d..91c9a2a 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,6 +22,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.util.MacAddressUtils;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -122,11 +124,11 @@
for (MacAddress mac : multicastAddresses) {
String msg = mac.toString() + " expected to be a multicast address";
- assertTrue(msg, mac.isMulticastAddress());
+ assertTrue(msg, MacAddressUtils.isMulticastAddress(mac));
}
for (MacAddress mac : unicastAddresses) {
String msg = mac.toString() + " expected not to be a multicast address";
- assertFalse(msg, mac.isMulticastAddress());
+ assertFalse(msg, MacAddressUtils.isMulticastAddress(mac));
}
}
@@ -156,7 +158,7 @@
public void testMacAddressConversions() {
final int iterations = 10000;
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
String stringRepr = mac.toString();
byte[] bytesRepr = mac.toByteArray();
@@ -188,7 +190,7 @@
final String expectedLocalOui = "26:5f:78";
final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0");
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress(base, r);
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r);
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -199,7 +201,7 @@
}
for (int i = 0; i < iterations; i++) {
- MacAddress mac = MacAddress.createRandomUnicastAddress();
+ MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
String stringRepr = mac.toString();
assertTrue(stringRepr + " expected to be a locally assigned address",
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index c16a0f4..735fa7c 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@
@Test
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@
@Test
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@
assertEquals(0, stats.size());
assertEquals(4, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
assertEquals(4, stats.size());
assertEquals(4, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
assertEquals(9, stats.size());
@@ -193,8 +193,8 @@
public void testCombineExisting() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 10);
- stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
- stats.addValues(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
-128L, -1L, -1);
@@ -215,12 +215,12 @@
@Test
public void testSubtractIdenticalData() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats result = after.subtract(before);
@@ -234,12 +234,12 @@
@Test
public void testSubtractIdenticalRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
final NetworkStats result = after.subtract(before);
@@ -253,13 +253,13 @@
@Test
public void testSubtractNewRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
final NetworkStats result = after.subtract(before);
@@ -275,11 +275,11 @@
@Test
public void testSubtractMissingRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
final NetworkStats after = new NetworkStats(TEST_START, 1)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
final NetworkStats result = after.subtract(before);
@@ -293,40 +293,40 @@
@Test
public void testTotalBytes() throws Exception {
final NetworkStats iface = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
assertEquals(384L, iface.getTotalBytes());
final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidSet.getTotalBytes());
final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
assertEquals(64L, uidTag.getTotalBytes());
final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidMetered.getTotalBytes());
final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidRoaming.getTotalBytes());
}
@@ -343,11 +343,11 @@
@Test
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
- .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -361,19 +361,19 @@
@Test
public void testGroupedByIface() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@
@Test
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
first.combineAllValues(second);
@@ -421,19 +421,19 @@
@Test
public void testGetTotal() {
final NetworkStats stats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@
assertEquals(0, after.size());
// Test 1 item stats.
- before.addValues(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+ before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
after = before.clone();
after.removeUids(new int[0]);
assertEquals(1, after.size());
@@ -469,12 +469,12 @@
assertEquals(0, after.size());
// Append remaining test items.
- before.addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+ before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
assertEquals(7, before.size());
// Test remove with empty uid list.
@@ -503,14 +503,61 @@
}
@Test
+ public void testRemoveEmptyEntries() throws Exception {
+ // Test empty stats.
+ final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3);
+ assertEquals(0, statsEmpty.removeEmptyEntries().size());
+
+ // Test stats with non-zero entry.
+ final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1)
+ .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+ assertEquals(1, statsNonZero.size());
+ final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries();
+ assertEquals(1, expectedNonZero.size());
+ assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+
+ // Test stats with empty entry.
+ final NetworkStats statsZero = new NetworkStats(TEST_START, 1)
+ .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+ assertEquals(1, statsZero.size());
+ final NetworkStats expectedZero = statsZero.removeEmptyEntries();
+ assertEquals(1, statsZero.size()); // Assert immutable.
+ assertEquals(0, expectedZero.size());
+
+ // Test stats with multiple entries.
+ final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0)
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L);
+ assertEquals(9, statsMultiple.size());
+ final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries();
+ assertEquals(9, statsMultiple.size()); // Assert immutable.
+ assertEquals(7, expectedMultiple.size());
+ assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L);
+
+ // Test stats with multiple empty entries.
+ assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size());
+ assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size());
+ }
+
+ @Test
public void testClone() throws Exception {
final NetworkStats original = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// make clone and mutate original
final NetworkStats clone = original.clone();
- original.addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+ original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
assertEquals(3, original.size());
assertEquals(2, clone.size());
@@ -523,8 +570,8 @@
public void testAddWhenEmpty() throws Exception {
final NetworkStats red = new NetworkStats(TEST_START, -1);
final NetworkStats blue = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// We're mostly checking that we don't crash
red.combineAllValues(blue);
@@ -537,39 +584,39 @@
final String underlyingIface = "wlan0";
final int testTag1 = 8888;
NetworkStats delta = new NetworkStats(TEST_START, 17)
- .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
- .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
- .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
- // VPN package also uses some traffic through unprotected network.
- .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
- .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- // Tag entries
- .addValues(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
- .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
- // Irrelevant entries
- .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
- // Underlying Iface entries
- .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
- .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
- 299L /* smaller than sum(tun0) */, 0L)
- .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
+ .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
+ // VPN package also uses some traffic through unprotected network.
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
+ .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ // Tag entries
+ .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
+ // Irrelevant entries
+ .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
+ // Underlying Iface entries
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
+ .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
+ 299L /* smaller than sum(tun0) */, 0L)
+ .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
- delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface});
+ delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed.
@@ -634,21 +681,21 @@
final String tunIface = "tun0";
final String underlyingIface = "wlan0";
NetworkStats delta = new NetworkStats(TEST_START, 9)
- // 2 different apps sent/receive data via tun0.
- .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
- .addValues(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
- // VPN package resends data through the tunnel (with exaggerated overhead)
- .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
- // 1 app already has some traffic on the underlying interface, the other doesn't yet
- .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
- // Traffic through the underlying interface via the vpn app.
- // This test should redistribute this data correctly.
- .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
+ // 2 different apps sent/receive data via tun0.
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
+ .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
+ // VPN package resends data through the tunnel (with exaggerated overhead)
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
+ // 1 app already has some traffic on the underlying interface, the other doesn't yet
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
+ // Traffic through the underlying interface via the vpn app.
+ // This test should redistribute this data correctly.
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(9, delta.size());
@@ -697,9 +744,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(3, stats.size());
@@ -724,9 +771,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
assertEquals(2, stats.size());
@@ -755,10 +802,10 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3)
- .addValues(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
assertEquals(3, stats.size());
@@ -778,8 +825,8 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(entry1)
- .addValues(entry2);
+ .insertEntry(entry1)
+ .insertEntry(entry2);
stats.filter(UID_ALL, new String[] { }, TAG_ALL);
assertEquals(0, stats.size());
@@ -802,9 +849,9 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, testTag);
assertEquals(2, stats.size());
@@ -831,10 +878,10 @@
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3)
- .addValues(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filterDebugEntries();
@@ -862,8 +909,8 @@
13805 /* txPackets */,
0 /* operations */);
- // Traffic measured for the root uid on the base interface if eBPF is in use.
- final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+ // Traffic measured for the root uid on the base interface.
+ final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry(
baseIface, rootUid, SET_DEFAULT, TAG_NONE,
163577 /* rxBytes */,
187 /* rxPackets */,
@@ -871,17 +918,6 @@
97 /* txPackets */,
0 /* operations */);
- // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
- // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
- // overhead (20 bytes per packet), in rx direction.
- final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
- baseIface, rootUid, SET_DEFAULT, TAG_NONE,
- 31113087 /* rxBytes */,
- 22588 /* rxPackets */,
- 17607 /* txBytes */,
- 97 /* txPackets */,
- 0 /* operations */);
-
final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
otherIface, appUid, SET_DEFAULT, TAG_NONE,
2600 /* rxBytes */,
@@ -890,21 +926,14 @@
3 /* txPackets */,
0 /* operations */);
- final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
- .addValues(appEntry)
- .addValues(xtRootUidEntry)
- .addValues(otherEntry);
+ final NetworkStats stats = new NetworkStats(TEST_START, 3)
+ .insertEntry(appEntry)
+ .insertEntry(rootUidEntry)
+ .insertEntry(otherEntry);
- final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
- .addValues(appEntry)
- .addValues(ebpfRootUidEntry)
- .addValues(otherEntry);
+ stats.apply464xlatAdjustments(stackedIface);
- statsXt.apply464xlatAdjustments(stackedIface, false);
- statsEbpf.apply464xlatAdjustments(stackedIface, true);
-
- assertEquals(3, statsXt.size());
- assertEquals(3, statsEbpf.size());
+ assertEquals(3, stats.size());
final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
v4Iface, appUid, SET_DEFAULT, TAG_NONE,
30949510,
@@ -919,12 +948,9 @@
17607,
97,
0);
- assertEquals(expectedAppUid, statsXt.getValues(0, null));
- assertEquals(expectedRootUid, statsXt.getValues(1, null));
- assertEquals(otherEntry, statsXt.getValues(2, null));
- assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
- assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
- assertEquals(otherEntry, statsEbpf.getValues(2, null));
+ assertEquals(expectedAppUid, stats.getValues(0, null));
+ assertEquals(expectedRootUid, stats.getValues(1, null));
+ assertEquals(otherEntry, stats.getValues(2, null));
}
@Test
@@ -945,11 +971,11 @@
0 /* operations */);
NetworkStats stats = new NetworkStats(TEST_START, 2)
- .addValues(firstEntry)
- .addValues(secondEntry);
+ .insertEntry(firstEntry)
+ .insertEntry(secondEntry);
// Empty map: no adjustment
- stats.apply464xlatAdjustments(new ArrayMap<>(), false);
+ stats.apply464xlatAdjustments(new ArrayMap<>());
assertEquals(2, stats.size());
assertEquals(firstEntry, stats.getValues(0, null));
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
new file mode 100644
index 0000000..9ba56e4
--- /dev/null
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 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.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkIdentity.SUBTYPE_COMBINED
+import android.net.NetworkIdentity.buildNetworkIdentity
+import android.net.NetworkStats.DEFAULT_NETWORK_ALL
+import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
+import android.net.NetworkTemplate.NETWORK_TYPE_ALL
+import android.net.NetworkTemplate.buildTemplateMobileWithRatType
+import android.telephony.TelephonyManager
+import com.android.testutils.assertParcelSane
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+private const val TEST_IMSI1 = "imsi1"
+private const val TEST_IMSI2 = "imsi2"
+private const val TEST_SSID1 = "ssid1"
+
+@RunWith(JUnit4::class)
+class NetworkTemplateTest {
+ private val mockContext = mock(Context::class.java)
+
+ private fun buildMobileNetworkState(subscriberId: String): NetworkState =
+ buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId)
+ private fun buildWifiNetworkState(ssid: String): NetworkState =
+ buildNetworkState(TYPE_WIFI, ssid = ssid)
+
+ private fun buildNetworkState(
+ type: Int,
+ subscriberId: String? = null,
+ ssid: String? = null
+ ): NetworkState {
+ val info = mock(NetworkInfo::class.java)
+ doReturn(type).`when`(info).type
+ doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
+ val lp = LinkProperties()
+ val caps = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+ }
+ return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+ }
+
+ private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
+ assertTrue(matches(ident), "$this does not match $ident")
+
+ private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) =
+ assertFalse(matches(ident), "$this should match $ident")
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testRatTypeGroupMatches() {
+ val stateMobile = buildMobileNetworkState(TEST_IMSI1)
+ // Build UMTS template that matches mobile identities with RAT in the same
+ // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
+ val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS)
+ // Build normal template that matches mobile identities with any RAT and IMSI.
+ val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL)
+ // Build template with UNKNOWN RAT that matches mobile identities with RAT that
+ // cannot be determined.
+ val templateUnknown =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN)
+
+ val identUmts = buildNetworkIdentity(
+ mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identHsdpa = buildNetworkIdentity(
+ mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA)
+ val identLte = buildNetworkIdentity(
+ mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE)
+ val identCombined = buildNetworkIdentity(
+ mockContext, stateMobile, false, SUBTYPE_COMBINED)
+ val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
+ false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identWifi = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_SSID1), true, 0)
+
+ // Assert that identity with the same RAT matches.
+ templateUmts.assertMatches(identUmts)
+ templateAll.assertMatches(identUmts)
+ templateUnknown.assertDoesNotMatch(identUmts)
+ // Assert that identity with the RAT within the same group matches.
+ templateUmts.assertMatches(identHsdpa)
+ templateAll.assertMatches(identHsdpa)
+ templateUnknown.assertDoesNotMatch(identHsdpa)
+ // Assert that identity with the RAT out of the same group only matches template with
+ // NETWORK_TYPE_ALL.
+ templateUmts.assertDoesNotMatch(identLte)
+ templateAll.assertMatches(identLte)
+ templateUnknown.assertDoesNotMatch(identLte)
+ // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL
+ // and NETWORK_TYPE_UNKNOWN.
+ templateUmts.assertDoesNotMatch(identCombined)
+ templateAll.assertMatches(identCombined)
+ templateUnknown.assertMatches(identCombined)
+ // Assert that identity with different IMSI matches.
+ templateUmts.assertMatches(identImsi2)
+ templateAll.assertMatches(identImsi2)
+ templateUnknown.assertDoesNotMatch(identImsi2)
+ // Assert that wifi identity does not match.
+ templateUmts.assertDoesNotMatch(identWifi)
+ templateAll.assertDoesNotMatch(identWifi)
+ templateUnknown.assertDoesNotMatch(identWifi)
+ }
+
+ @Test
+ fun testParcelUnparcel() {
+ val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE)
+ val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, 0)
+ assertParcelSane(templateMobile, 8)
+ assertParcelSane(templateWifi, 8)
+ }
+
+ // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with
+ // TelephonyManager#NETWORK_TYPE_* constants.
+ @Test
+ fun testNetworkTypeConstants() {
+ for (ratType in TelephonyManager.getAllNetworkTypes()) {
+ assertNotEquals(NETWORK_TYPE_ALL, ratType)
+ assertNotEquals(NETWORK_TYPE_5G_NSA, ratType)
+ }
+ }
+}
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 7748288..3158cc8 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,10 +16,24 @@
package android.net;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
import androidx.test.runner.AndroidJUnit4;
+import libcore.io.IoUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -125,4 +139,50 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
+
+ private static void expectSocketSuccess(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ } catch (ErrnoException e) {
+ fail(msg + e.getMessage());
+ }
+ }
+
+ private static void expectSocketPemissionError(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ fail(msg);
+ } catch (ErrnoException e) {
+ assertEquals(msg, e.errno, EPERM);
+ }
+ }
+
+ private static void expectHasNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
+ AF_INET, SOCK_DGRAM);
+ expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ private static void expectNoNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
+ AF_INET, SOCK_DGRAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ @Test
+ public void testSetAllowNetworkingForProcess() {
+ expectHasNetworking();
+ NetworkUtils.setAllowNetworkingForProcess(false);
+ expectNoNetworking();
+ NetworkUtils.setAllowNetworkingForProcess(true);
+ expectHasNetworking();
+ }
}
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
index 583d3fd..cea8c57 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -16,14 +16,12 @@
package android.net;
+import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import android.net.SocketKeepalive.InvalidPacketException;
-
-import com.android.internal.util.TestUtils;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -68,10 +66,10 @@
fail("InvalidPacketException: " + e);
}
- assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.srcAddress);
- assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.dstAddress);
- assertEquals(testInfo.srcPort, resultData.srcPort);
- assertEquals(testInfo.dstPort, resultData.dstPort);
+ assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.getSrcAddress());
+ assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.getDstAddress());
+ assertEquals(testInfo.srcPort, resultData.getSrcPort());
+ assertEquals(testInfo.dstPort, resultData.getDstPort());
assertEquals(testInfo.seq, resultData.tcpSeq);
assertEquals(testInfo.ack, resultData.tcpAck);
assertEquals(testInfo.rcvWnd, resultData.tcpWnd);
@@ -79,7 +77,7 @@
assertEquals(testInfo.tos, resultData.ipTos);
assertEquals(testInfo.ttl, resultData.ipTtl);
- TestUtils.assertParcelingIsLossless(resultData);
+ assertParcelingIsLossless(resultData);
final byte[] packet = resultData.getPacket();
// IP version and IHL
diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
new file mode 100644
index 0000000..efb9203
--- /dev/null
+++ b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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 static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.WifiNetworkSpecifier;
+import android.telephony.SubscriptionManager;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link android.net.TelephonyNetworkSpecifier}.
+ */
+@SmallTest
+public class TelephonyNetworkSpecifierTest {
+ private static final int TEST_SUBID = 5;
+ private static final String TEST_SSID = "Test123";
+
+ /**
+ * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier
+ * without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
+ */
+ @Test
+ public void testBuilderBuildWithDefault() {
+ try {
+ new TelephonyNetworkSpecifier.Builder().build();
+ } catch (IllegalArgumentException iae) {
+ // expected, test pass
+ }
+ }
+
+ /**
+ * Validate that no exception will be thrown even if pass invalid subscription id to
+ * {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}.
+ */
+ @Test
+ public void testBuilderBuildWithInvalidSubId() {
+ TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .build();
+ assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ /**
+ * Validate the correctness of TelephonyNetworkSpecifier when provide valid subId.
+ */
+ @Test
+ public void testBuilderBuildWithValidSubId() {
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUBID)
+ .build();
+ assertEquals(TEST_SUBID, specifier.getSubscriptionId());
+ }
+
+ /**
+ * Validate that parcel marshalling/unmarshalling works.
+ */
+ @Test
+ public void testParcel() {
+ TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUBID)
+ .build();
+ assertParcelSane(specifier, 1 /* fieldCount */);
+ }
+
+ /**
+ * Validate the behavior of method canBeSatisfiedBy().
+ */
+ @Test
+ public void testCanBeSatisfiedBy() {
+ final TelephonyNetworkSpecifier tns1 = new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUBID)
+ .build();
+ final TelephonyNetworkSpecifier tns2 = new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(TEST_SUBID)
+ .build();
+ final WifiNetworkSpecifier wns = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .build();
+ final MatchAllNetworkSpecifier mans = new MatchAllNetworkSpecifier();
+
+ // Test equality
+ assertEquals(tns1, tns2);
+ assertTrue(tns1.canBeSatisfiedBy(tns1));
+ assertTrue(tns1.canBeSatisfiedBy(tns2));
+
+ // Test other edge cases.
+ assertFalse(tns1.canBeSatisfiedBy(null));
+ assertFalse(tns1.canBeSatisfiedBy(wns));
+ assertTrue(tns1.canBeSatisfiedBy(mans));
+ }
+}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
new file mode 100644
index 0000000..95a7942
--- /dev/null
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.net.VpnProfile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnManagerTest {
+ private static final String PKG_NAME = "fooPackage";
+
+ private static final String SESSION_NAME_STRING = "testSession";
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+
+ private IConnectivityManager mMockCs;
+ private VpnManager mVpnManager;
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return PKG_NAME;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mMockCs = mock(IConnectivityManager.class);
+ mVpnManager = new VpnManager(mMockContext, mMockCs);
+ }
+
+ @Test
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true);
+
+ // Expect there to be no intent returned, as consent has already been granted.
+ assertNull(mVpnManager.provisionVpnProfile(profile));
+ verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ }
+
+ @Test
+ public void testProvisionVpnProfileNeedsConsent() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
+
+ // Expect intent to be returned, as consent has not already been granted.
+ final Intent intent = mVpnManager.provisionVpnProfile(profile);
+ assertNotNull(intent);
+
+ final ComponentName expectedComponentName =
+ ComponentName.unflattenFromString(
+ "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
+ assertEquals(expectedComponentName, intent.getComponent());
+ verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ }
+
+ @Test
+ public void testDeleteProvisionedVpnProfile() throws Exception {
+ mVpnManager.deleteProvisionedVpnProfile();
+ verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
+ }
+
+ @Test
+ public void testStartProvisionedVpnProfile() throws Exception {
+ mVpnManager.startProvisionedVpnProfile();
+ verify(mMockCs).startVpnProfile(eq(PKG_NAME));
+ }
+
+ @Test
+ public void testStopProvisionedVpnProfile() throws Exception {
+ mVpnManager.stopProvisionedVpnProfile();
+ verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
+ }
+
+ private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
+ return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+ .setBypassable(true)
+ .setMaxMtu(1300)
+ .setMetered(true)
+ .setAuthPsk(PSK_BYTES)
+ .build();
+ }
+}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 1a3ea60..02f5286 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -54,7 +54,7 @@
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000);
- builder.setGroupHint("groupHint");
+ builder.setCluster("groupHint");
builder.setDnsAddresses(Arrays.asList(
InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
InetAddress.getByName("6.7.8.9")));
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java
index 2d2bccb..cf7587a 100644
--- a/tests/net/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/net/java/android/net/nsd/NsdManagerTest.java
@@ -16,8 +16,6 @@
package android.net.nsd;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -40,6 +38,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.AsyncChannel;
+import com.android.testutils.HandlerUtilsKt;
import org.junit.After;
import org.junit.Before;
@@ -74,7 +73,7 @@
@After
public void tearDown() throws Exception {
- mServiceHandler.waitForIdle(mTimeoutMs);
+ HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs);
mServiceHandler.chan.disconnect();
mServiceHandler.stop();
if (mManager != null) {
@@ -334,7 +333,7 @@
}
int verifyRequest(int expectedMessageType) {
- mServiceHandler.waitForIdle(mTimeoutMs);
+ HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs);
verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
reset(mServiceHandler);
Message received = mServiceHandler.getLastMessage();
@@ -366,10 +365,6 @@
lastMessage.copyFrom(msg);
}
- void waitForIdle(long timeoutMs) {
- waitForIdleHandler(this, timeoutMs);
- }
-
@Override
public void handleMessage(Message msg) {
setLastMessage(msg);
diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/tests/net/java/android/net/util/DnsUtilsTest.java
index 42e340b..b626db8 100644
--- a/tests/net/java/android/net/util/DnsUtilsTest.java
+++ b/tests/net/java/android/net/util/DnsUtilsTest.java
@@ -57,24 +57,38 @@
@Test
public void testRfc6724Comparator() {
final List<DnsUtils.SortableAddress> test = Arrays.asList(
- makeSortableAddress("216.58.200.36"), // Ipv4
- makeSortableAddress("2404:6800:4008:801::2004"), // global
- makeSortableAddress("::1"), // loop back
- makeSortableAddress("fe80::c46f:1cff:fe04:39b4"), // link local
- makeSortableAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6
- makeSortableAddress("2001::47c1"), // teredo tunneling
- makeSortableAddress("::216.58.200.36"), // IPv4-compatible
- makeSortableAddress("3ffe::1234:5678")); // 6bone
+ // Ipv4
+ makeSortableAddress("216.58.200.36", "192.168.1.1"),
+ // global with different scope src
+ makeSortableAddress("2404:6800:4008:801::2004", "fe80::1111:2222"),
+ // global without src addr
+ makeSortableAddress("2404:6800:cafe:801::1"),
+ // loop back
+ makeSortableAddress("::1", "::1"),
+ // link local
+ makeSortableAddress("fe80::c46f:1cff:fe04:39b4", "fe80::1"),
+ // teredo tunneling
+ makeSortableAddress("2001::47c1", "2001::2"),
+ // 6bone without src addr
+ makeSortableAddress("3ffe::1234:5678"),
+ // IPv4-compatible
+ makeSortableAddress("::216.58.200.36", "::216.58.200.9"),
+ // 6bone
+ makeSortableAddress("3ffe::1234:5678", "3ffe::1234:1"),
+ // IPv4-mapped IPv6
+ makeSortableAddress("::ffff:192.168.95.7", "::ffff:192.168.95.1"));
final List<InetAddress> expected = Arrays.asList(
stringToAddress("::1"), // loop back
stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local
- stringToAddress("2404:6800:4008:801::2004"), // global
stringToAddress("216.58.200.36"), // Ipv4
- stringToAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6
+ stringToAddress("::ffff:192.168.95.7"), // IPv4-mapped IPv6
stringToAddress("2001::47c1"), // teredo tunneling
- stringToAddress("::216.58.200.36"), // IPv4-compatible
- stringToAddress("3ffe::1234:5678")); // 6bone
+ stringToAddress("::216.58.200.36"), // IPv4-compatible
+ stringToAddress("3ffe::1234:5678"), // 6bone
+ stringToAddress("2404:6800:4008:801::2004"), // global with different scope src
+ stringToAddress("2404:6800:cafe:801::1"), // global without src addr
+ stringToAddress("3ffe::1234:5678")); // 6bone without src addr
Collections.sort(test, new DnsUtils.Rfc6724Comparator());
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 0000000..e5daa71
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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 com.android.internal.net;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.IpSecAlgorithm;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class VpnProfileTest {
+ private static final String DUMMY_PROFILE_KEY = "Test";
+
+ private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
+ private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+
+ @Test
+ public void testDefaults() throws Exception {
+ final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+ assertEquals(DUMMY_PROFILE_KEY, p.key);
+ assertEquals("", p.name);
+ assertEquals(VpnProfile.TYPE_PPTP, p.type);
+ assertEquals("", p.server);
+ assertEquals("", p.username);
+ assertEquals("", p.password);
+ assertEquals("", p.dnsServers);
+ assertEquals("", p.searchDomains);
+ assertEquals("", p.routes);
+ assertTrue(p.mppe);
+ assertEquals("", p.l2tpSecret);
+ assertEquals("", p.ipsecIdentifier);
+ assertEquals("", p.ipsecSecret);
+ assertEquals("", p.ipsecUserCert);
+ assertEquals("", p.ipsecCaCert);
+ assertEquals("", p.ipsecServerCert);
+ assertEquals(null, p.proxy);
+ assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+ assertFalse(p.isBypassable);
+ assertFalse(p.isMetered);
+ assertEquals(1360, p.maxMtu);
+ assertFalse(p.areAuthParamsInline);
+ assertFalse(p.isRestrictedToTestNetworks);
+ }
+
+ private VpnProfile getSampleIkev2Profile(String key) {
+ final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
+
+ p.name = "foo";
+ p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ p.server = "bar";
+ p.username = "baz";
+ p.password = "qux";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.l2tpSecret = "";
+ p.ipsecIdentifier = "quux";
+ p.ipsecSecret = "quuz";
+ p.ipsecUserCert = "corge";
+ p.ipsecCaCert = "grault";
+ p.ipsecServerCert = "garply";
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(
+ getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+ final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ modified.maxMtu--;
+ assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmValueDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmListDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testEncodeDecodeTooManyValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final byte[] tooManyValues =
+ (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+ }
+
+ private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
+ // Sort to ensure when we remove, we can do it from greatest first.
+ Arrays.sort(missingIndices);
+
+ final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
+ final List<String> parts =
+ new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
+
+ // Remove from back first to ensure indexing is consistent.
+ for (int i = missingIndices.length - 1; i >= 0; i--) {
+ parts.remove(missingIndices[i]);
+ }
+
+ return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
+ }
+
+ @Test
+ public void testEncodeDecodeInvalidNumberOfValues() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_AUTH_PARAMS_INLINE,
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
+ }
+
+ @Test
+ public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
+ final String tooFewValues =
+ getEncodedDecodedIkev2ProfileMissingValues(
+ ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+
+ // Verify decoding without isRestrictedToTestNetworks defaults to false
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+ assertFalse(decoded.isRestrictedToTestNetworks);
+ }
+
+ @Test
+ public void testEncodeDecodeLoginsNotSaved() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ profile.saveLogin = false;
+
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertNotEquals(profile, decoded);
+
+ // Add the username/password back, everything else must be equal.
+ decoded.username = profile.username;
+ decoded.password = profile.password;
+ assertEquals(profile, decoded);
+ }
+}
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java
index 01fb0df..d2fbdce 100644
--- a/tests/net/java/com/android/internal/util/BitUtilsTest.java
+++ b/tests/net/java/com/android/internal/util/BitUtilsTest.java
@@ -21,11 +21,14 @@
import static com.android.internal.util.BitUtils.getUint16;
import static com.android.internal.util.BitUtils.getUint32;
import static com.android.internal.util.BitUtils.getUint8;
+import static com.android.internal.util.BitUtils.packBits;
import static com.android.internal.util.BitUtils.uint16;
import static com.android.internal.util.BitUtils.uint32;
import static com.android.internal.util.BitUtils.uint8;
+import static com.android.internal.util.BitUtils.unpackBits;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -34,6 +37,8 @@
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -110,20 +115,66 @@
@Test
public void testUnsignedGetters() {
- ByteBuffer b = ByteBuffer.allocate(4);
- b.putInt(0xffff);
+ ByteBuffer b = ByteBuffer.allocate(4);
+ b.putInt(0xffff);
- assertEquals(0x0, getUint8(b, 0));
- assertEquals(0x0, getUint8(b, 1));
- assertEquals(0xff, getUint8(b, 2));
- assertEquals(0xff, getUint8(b, 3));
+ assertEquals(0x0, getUint8(b, 0));
+ assertEquals(0x0, getUint8(b, 1));
+ assertEquals(0xff, getUint8(b, 2));
+ assertEquals(0xff, getUint8(b, 3));
- assertEquals(0x0, getUint16(b, 0));
- assertEquals(0xffff, getUint16(b, 2));
+ assertEquals(0x0, getUint16(b, 0));
+ assertEquals(0xffff, getUint16(b, 2));
- b.rewind();
- b.putInt(0xffffffff);
- assertEquals(0xffffffffL, getUint32(b, 0));
+ b.rewind();
+ b.putInt(0xffffffff);
+ assertEquals(0xffffffffL, getUint32(b, 0));
+ }
+
+ @Test
+ public void testBitsPacking() {
+ BitPackingTestCase[] testCases = {
+ new BitPackingTestCase(0, ints()),
+ new BitPackingTestCase(1, ints(0)),
+ new BitPackingTestCase(2, ints(1)),
+ new BitPackingTestCase(3, ints(0, 1)),
+ new BitPackingTestCase(4, ints(2)),
+ new BitPackingTestCase(6, ints(1, 2)),
+ new BitPackingTestCase(9, ints(0, 3)),
+ new BitPackingTestCase(~Long.MAX_VALUE, ints(63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)),
+ };
+ for (BitPackingTestCase tc : testCases) {
+ int[] got = unpackBits(tc.packedBits);
+ assertTrue(
+ "unpackBits("
+ + tc.packedBits
+ + "): expected "
+ + Arrays.toString(tc.bits)
+ + " but got "
+ + Arrays.toString(got),
+ Arrays.equals(tc.bits, got));
+ }
+ for (BitPackingTestCase tc : testCases) {
+ long got = packBits(tc.bits);
+ assertEquals(
+ "packBits("
+ + Arrays.toString(tc.bits)
+ + "): expected "
+ + tc.packedBits
+ + " but got "
+ + got,
+ tc.packedBits,
+ got);
+ }
+
+ long[] moreTestCases = {
+ 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(),
+ };
+ for (long l : moreTestCases) {
+ assertEquals(l, packBits(unpackBits(l)));
+ }
}
static byte[] bytes(int b1, int b2, int b3, int b4) {
@@ -133,4 +184,18 @@
static byte b(int i) {
return (byte) i;
}
+
+ static int[] ints(int... array) {
+ return array;
+ }
+
+ static class BitPackingTestCase {
+ final int[] bits;
+ final long packedBits;
+
+ BitPackingTestCase(long packedBits, int[] bits) {
+ this.bits = bits;
+ this.packedBits = packedBits;
+ }
+ }
}
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java
index eff334f..d06095a 100644
--- a/tests/net/java/com/android/internal/util/RingBufferTest.java
+++ b/tests/net/java/com/android/internal/util/RingBufferTest.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
@@ -25,9 +26,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Arrays;
-import java.util.Objects;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RingBufferTest {
@@ -36,7 +34,7 @@
public void testEmptyRingBuffer() {
RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
- assertArraysEqual(new String[0], buffer.toArray());
+ assertArrayEquals(new String[0], buffer.toArray());
}
@Test
@@ -65,7 +63,7 @@
buffer.append("e");
String[] expected = {"a", "b", "c", "d", "e"};
- assertArraysEqual(expected, buffer.toArray());
+ assertArrayEquals(expected, buffer.toArray());
}
@Test
@@ -73,19 +71,19 @@
RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
buffer.append("a");
- assertArraysEqual(new String[]{"a"}, buffer.toArray());
+ assertArrayEquals(new String[]{"a"}, buffer.toArray());
buffer.append("b");
- assertArraysEqual(new String[]{"b"}, buffer.toArray());
+ assertArrayEquals(new String[]{"b"}, buffer.toArray());
buffer.append("c");
- assertArraysEqual(new String[]{"c"}, buffer.toArray());
+ assertArrayEquals(new String[]{"c"}, buffer.toArray());
buffer.append("d");
- assertArraysEqual(new String[]{"d"}, buffer.toArray());
+ assertArrayEquals(new String[]{"d"}, buffer.toArray());
buffer.append("e");
- assertArraysEqual(new String[]{"e"}, buffer.toArray());
+ assertArrayEquals(new String[]{"e"}, buffer.toArray());
}
@Test
@@ -100,7 +98,7 @@
buffer.append("e");
String[] expected1 = {"a", "b", "c", "d", "e"};
- assertArraysEqual(expected1, buffer.toArray());
+ assertArrayEquals(expected1, buffer.toArray());
String[] expected2 = new String[capacity];
int firstIndex = 0;
@@ -111,22 +109,22 @@
buffer.append("x");
expected2[i] = "x";
}
- assertArraysEqual(expected2, buffer.toArray());
+ assertArrayEquals(expected2, buffer.toArray());
buffer.append("x");
expected2[firstIndex] = "x";
- assertArraysEqual(expected2, buffer.toArray());
+ assertArrayEquals(expected2, buffer.toArray());
for (int i = 0; i < 10; i++) {
for (String s : expected2) {
buffer.append(s);
}
}
- assertArraysEqual(expected2, buffer.toArray());
+ assertArrayEquals(expected2, buffer.toArray());
buffer.append("a");
expected2[lastIndex] = "a";
- assertArraysEqual(expected2, buffer.toArray());
+ assertArrayEquals(expected2, buffer.toArray());
}
@Test
@@ -143,7 +141,7 @@
expected[i] = new DummyClass1();
expected[i].x = capacity * i;
}
- assertArraysEqual(expected, buffer.toArray());
+ assertArrayEquals(expected, buffer.toArray());
for (int i = 0; i < capacity; ++i) {
if (actual[i] != buffer.getNextSlot()) {
@@ -177,18 +175,4 @@
}
private static final class DummyClass3 {}
-
- static <T> void assertArraysEqual(T[] expected, T[] got) {
- if (expected.length != got.length) {
- fail(Arrays.toString(expected) + " and " + Arrays.toString(got)
- + " did not have the same length");
- }
-
- for (int i = 0; i < expected.length; i++) {
- if (!Objects.equals(expected[i], got[i])) {
- fail(Arrays.toString(expected) + " and " + Arrays.toString(got)
- + " were not equal");
- }
- }
- }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 945fcfa..385005f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -21,8 +21,12 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -31,13 +35,13 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_VPN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -71,12 +75,20 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.IPPROTO_TCP;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
-import static com.android.internal.util.TestUtils.waitForIdleLooper;
-import static com.android.internal.util.TestUtils.waitForIdleSerialExecutor;
+import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
+import static com.android.testutils.ConcurrentUtilsKt.await;
+import static com.android.testutils.ConcurrentUtilsKt.durationOf;
+import static com.android.testutils.ExceptionUtils.ignoreExceptions;
+import static com.android.testutils.HandlerUtilsKt.waitForIdleSerialExecutor;
+import static com.android.testutils.MiscAssertsKt.assertContainsExactly;
+import static com.android.testutils.MiscAssertsKt.assertEmpty;
+import static com.android.testutils.MiscAssertsKt.assertLength;
+import static com.android.testutils.MiscAssertsKt.assertRunsInAtMost;
+import static com.android.testutils.MiscAssertsKt.assertThrows;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -84,13 +96,19 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -102,7 +120,10 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.annotation.NonNull;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -116,19 +137,26 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.location.LocationManager;
+import android.net.CaptivePortalData;
+import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.DataStallReportParcelable;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
+import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -138,34 +166,39 @@
import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
import android.net.NetworkStackClient;
import android.net.NetworkState;
+import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.UidRange;
+import android.net.Uri;
+import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
+import android.os.BadParcelableException;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -175,7 +208,9 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.KeyStore;
import android.system.Os;
+import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -183,27 +218,33 @@
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
-import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkStatsFactory;
+import com.android.testutils.ExceptionUtils;
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.RecorderCallback.CallbackEntry;
+import com.android.testutils.TestableNetworkCallback;
import org.junit.After;
import org.junit.Before;
@@ -224,11 +265,11 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -241,6 +282,9 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import kotlin.reflect.KClass;
/**
* Tests for {@link ConnectivityService}.
@@ -254,36 +298,51 @@
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 250;
+ private static final int TEST_LINGER_DELAY_MS = 300;
// Chosen to be less than the linger timeout. This ensures that we can distinguish between a
// LOST callback that arrives immediately and a LOST callback that arrives after the linger
// timeout. For this, our assertions should run fast enough to leave less than
// (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
- private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
// complete before callbacks are verified.
- private final static int TEST_REQUEST_TIMEOUT_MS = 150;
+ private static final int TEST_REQUEST_TIMEOUT_MS = 150;
+
+ private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
+
+ private static final long TIMESTAMP = 1234L;
+
+ private static final int NET_ID = 110;
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
+ private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String INTERFACE_NAME = "interface";
+
private MockContext mServiceContext;
- private WrappedConnectivityService mService;
+ private HandlerThread mCsHandlerThread;
+ private ConnectivityService mService;
private WrappedConnectivityManager mCm;
- private MockNetworkAgent mWiFiNetworkAgent;
- private MockNetworkAgent mCellNetworkAgent;
- private MockNetworkAgent mEthernetNetworkAgent;
+ private TestNetworkAgentWrapper mWiFiNetworkAgent;
+ private TestNetworkAgentWrapper mCellNetworkAgent;
+ private TestNetworkAgentWrapper mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
private INetworkPolicyListener mPolicyListener;
+ private WrappedMultinetworkPolicyTracker mPolicyTracker;
+ private HandlerThread mAlarmManagerThread;
+ @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ @Mock IBatteryStats mBatteryStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@@ -291,6 +350,12 @@
@Mock PackageManager mPackageManager;
@Mock UserManager mUserManager;
@Mock NotificationManager mNotificationManager;
+ @Mock AlarmManager mAlarmManager;
+ @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
+ @Mock IBinder mIBinder;
+ @Mock LocationManager mLocationManager;
+ @Mock AppOpsManager mAppOpsManager;
+ @Mock TelephonyManager mTelephonyManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -317,9 +382,14 @@
private class MockContext extends BroadcastInterceptingContext {
private final MockContentResolver mContentResolver;
+ // Contains all registered receivers since this object was created. Useful to clear
+ // them when needed, as BroadcastInterceptingContext does not provide this facility.
+ private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>();
@Spy private Resources mResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
MockContext(Context base, ContentProvider settingsProvider) {
super(base);
@@ -330,6 +400,13 @@
"wifi,1,1,1,-1,true",
"mobile,0,0,0,-1,true",
"mobile_mms,2,0,2,60000,true",
+ "mobile_supl,3,0,2,60000,true",
+ });
+
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_wakeonlan_supported_interfaces))
+ .thenReturn(new String[]{
+ WIFI_WOL_IFNAME,
});
mContentResolver = new MockContentResolver();
@@ -361,8 +438,11 @@
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
- if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
+ if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
+ if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
+ if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
+ if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
return super.getSystemService(name);
}
@@ -381,40 +461,80 @@
return mPackageManager;
}
+ private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission);
+ return granted != null ? granted : ifAbsent.get();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return checkMockedPermission(
+ permission, () -> super.checkPermission(permission, pid, uid));
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkMockedPermission(
+ permission, () -> super.checkCallingOrSelfPermission(permission));
+ }
+
@Override
public void enforceCallingOrSelfPermission(String permission, String message) {
- // The mainline permission can only be held if signed with the network stack certificate
- // Skip testing for this permission.
- if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return;
- // All other permissions should be held by the test or unnecessary: check as normal to
- // make sure the code does not rely on unexpected permissions.
- super.enforceCallingOrSelfPermission(permission, message);
- }
- }
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
- public void waitForIdle(int timeoutMsAsInt) {
- long timeoutMs = timeoutMsAsInt;
- waitForIdleHandler(mService.mHandlerThread, timeoutMs);
- waitForIdle(mCellNetworkAgent, timeoutMs);
- waitForIdle(mWiFiNetworkAgent, timeoutMs);
- waitForIdle(mEthernetNetworkAgent, timeoutMs);
- waitForIdleHandler(mService.mHandlerThread, timeoutMs);
- waitForIdleLooper(ConnectivityThread.getInstanceLooper(), timeoutMs);
- }
-
- public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
- if (agent == null) {
- return;
+ if (!granted.equals(PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
}
- waitForIdleHandler(agent.mHandlerThread, timeoutMs);
+
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ mRegisteredReceivers.add(receiver);
+ return super.registerReceiver(receiver, filter);
+ }
+
+ public void clearRegisteredReceivers() {
+ // super.unregisterReceiver is a no-op for receivers that are not registered (because
+ // they haven't been registered or because they have already been unregistered).
+ // For the same reason, don't bother clearing mRegisteredReceivers.
+ for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv);
+ }
}
private void waitForIdle() {
- waitForIdle(TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ waitForIdle(mCellNetworkAgent, TIMEOUT_MS);
+ waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS);
+ waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
+ }
+
+ private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) {
+ if (agent == null) {
+ return;
+ }
+ agent.waitForIdle(timeoutMs);
}
@Test
- public void testWaitForIdle() {
+ public void testWaitForIdle() throws Exception {
final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng.
// Tests that waitForIdle returns immediately if the service is already idle.
@@ -423,8 +543,8 @@
}
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
Network n = mWiFiNetworkAgent.getNetwork();
@@ -441,10 +561,10 @@
// This test has an inherent race condition in it, and cannot be enabled for continuous testing
// or presubmit tests. It is kept for manual runs and documentation purposes.
@Ignore
- public void verifyThatNotWaitingForIdleCausesRaceConditions() {
+ public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
Network n = mWiFiNetworkAgent.getNetwork();
@@ -464,108 +584,52 @@
fail("expected race condition at least once in " + attempts + " attempts");
}
- private class MockNetworkAgent {
- private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS
- | NETWORK_VALIDATION_PROBE_HTTP
- | NETWORK_VALIDATION_PROBE_HTTPS;
- private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE
- | NETWORK_VALIDATION_RESULT_VALID;
- private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE
- | NETWORK_VALIDATION_PROBE_FALLBACK
- | NETWORK_VALIDATION_RESULT_PARTIAL;
+ private class TestNetworkAgentWrapper extends NetworkAgentWrapper {
private static final int VALIDATION_RESULT_INVALID = 0;
- private final INetworkMonitor mNetworkMonitor;
- private final NetworkInfo mNetworkInfo;
- private final NetworkCapabilities mNetworkCapabilities;
- private final HandlerThread mHandlerThread;
- private final ConditionVariable mDisconnected = new ConditionVariable();
+ private static final long DATA_STALL_TIMESTAMP = 10L;
+ private static final int DATA_STALL_DETECTION_METHOD = 1;
+
+ private INetworkMonitor mNetworkMonitor;
+ private INetworkMonitorCallbacks mNmCallbacks;
+ private int mNmValidationResult = VALIDATION_RESULT_INVALID;
+ private int mProbesCompleted;
+ private int mProbesSucceeded;
+ private String mNmValidationRedirectUrl = null;
+ private boolean mNmProvNotificationRequested = false;
+
private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
- private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
- private int mScore;
- private NetworkAgent mNetworkAgent;
- private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
- private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
- private Integer mExpectedKeepaliveSlot = null;
// Contains the redirectUrl from networkStatus(). Before reading, wait for
// mNetworkStatusReceived.
private String mRedirectUrl;
- private INetworkMonitorCallbacks mNmCallbacks;
- private int mNmValidationResult = VALIDATION_RESULT_BASE;
- private String mNmValidationRedirectUrl = null;
- private boolean mNmProvNotificationRequested = false;
-
- void setNetworkValid() {
- mNmValidationResult = VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
- }
-
- void setNetworkInvalid() {
- mNmValidationResult = VALIDATION_RESULT_INVALID;
- mNmValidationRedirectUrl = null;
- }
-
- void setNetworkPortal(String redirectUrl) {
- setNetworkInvalid();
- mNmValidationRedirectUrl = redirectUrl;
- }
-
- void setNetworkPartial() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL;
- mNmValidationRedirectUrl = null;
- }
-
- void setNetworkPartialValid() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
- }
-
- MockNetworkAgent(int transport) {
+ TestNetworkAgentWrapper(int transport) throws Exception {
this(transport, new LinkProperties());
}
- MockNetworkAgent(int transport, LinkProperties linkProperties) {
- final int type = transportToLegacyType(transport);
- final String typeName = ConnectivityManager.getNetworkTypeName(type);
- mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
- mNetworkCapabilities = new NetworkCapabilities();
- mNetworkCapabilities.addTransportType(transport);
- switch (transport) {
- case TRANSPORT_ETHERNET:
- mScore = 70;
- break;
- case TRANSPORT_WIFI:
- mScore = 60;
- break;
- case TRANSPORT_CELLULAR:
- mScore = 50;
- break;
- case TRANSPORT_WIFI_AWARE:
- mScore = 20;
- break;
- case TRANSPORT_VPN:
- mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
- mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
- break;
- default:
- throw new UnsupportedOperationException("unimplemented network type");
- }
- mHandlerThread = new HandlerThread("Mock-" + typeName);
- mHandlerThread.start();
+ TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
+ throws Exception {
+ super(transport, linkProperties, mServiceContext);
+ // Waits for the NetworkAgent to be registered, which includes the creation of the
+ // NetworkMonitor.
+ waitForIdle(TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
+ }
+
+ @Override
+ protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties)
+ throws Exception {
mNetworkMonitor = mock(INetworkMonitor.class);
+
final Answer validateAnswer = inv -> {
- new Thread(this::onValidationRequested).start();
+ new Thread(ignoreExceptions(this::onValidationRequested)).start();
return null;
};
- try {
- doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
- doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
+ doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
final ArgumentCaptor<Network> nmNetworkCaptor = ArgumentCaptor.forClass(Network.class);
final ArgumentCaptor<INetworkMonitorCallbacks> nmCbCaptor =
@@ -575,132 +639,50 @@
any() /* name */,
nmCbCaptor.capture());
- mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
- "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
- linkProperties, mScore, new NetworkMisc(), NetworkFactory.SerialNumber.NONE) {
- @Override
- public void unwanted() { mDisconnected.open(); }
-
- @Override
- public void startSocketKeepalive(Message msg) {
- int slot = msg.arg1;
- if (mExpectedKeepaliveSlot != null) {
- assertEquals((int) mExpectedKeepaliveSlot, slot);
- }
- onSocketKeepaliveEvent(slot, mStartKeepaliveError);
- }
-
- @Override
- public void stopSocketKeepalive(Message msg) {
- onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
- }
-
+ final InstrumentedNetworkAgent na = new InstrumentedNetworkAgent(this, linkProperties) {
@Override
public void networkStatus(int status, String redirectUrl) {
mRedirectUrl = redirectUrl;
mNetworkStatusReceived.open();
}
-
- @Override
- protected void preventAutomaticReconnect() {
- mPreventReconnectReceived.open();
- }
-
- @Override
- protected void addKeepalivePacketFilter(Message msg) {
- Log.i(TAG, "Add keepalive packet filter.");
- }
-
- @Override
- protected void removeKeepalivePacketFilter(Message msg) {
- Log.i(TAG, "Remove keepalive packet filter.");
- }
};
- assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId);
+ assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
- try {
- mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);
- // Waits for the NetworkAgent to be registered, which includes the creation of the
- // NetworkMonitor.
- waitForIdle();
+ return na;
}
- private void onValidationRequested() {
- try {
- if (mNmProvNotificationRequested
- && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) {
- mNmCallbacks.hideProvisioningNotification();
- mNmProvNotificationRequested = false;
- }
+ private void onValidationRequested() throws Exception {
+ if (mNmProvNotificationRequested
+ && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) {
+ mNmCallbacks.hideProvisioningNotification();
+ mNmProvNotificationRequested = false;
+ }
- mNmCallbacks.notifyNetworkTested(
- mNmValidationResult, mNmValidationRedirectUrl);
+ mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
+ final NetworkTestResultParcelable p = new NetworkTestResultParcelable();
+ p.result = mNmValidationResult;
+ p.probesAttempted = mProbesCompleted;
+ p.probesSucceeded = mProbesSucceeded;
+ p.redirectUrl = mNmValidationRedirectUrl;
+ p.timestampMillis = TIMESTAMP;
+ mNmCallbacks.notifyNetworkTestedWithExtras(p);
- if (mNmValidationRedirectUrl != null) {
- mNmCallbacks.showProvisioningNotification(
- "test_provisioning_notif_action", "com.android.test.package");
- mNmProvNotificationRequested = true;
- }
- } catch (RemoteException e) {
- fail(e.getMessage());
+ if (mNmValidationRedirectUrl != null) {
+ mNmCallbacks.showProvisioningNotification(
+ "test_provisioning_notif_action", TEST_PACKAGE_NAME);
+ mNmProvNotificationRequested = true;
}
}
- public void adjustScore(int change) {
- mScore += change;
- mNetworkAgent.sendNetworkScore(mScore);
- }
-
- public int getScore() {
- return mScore;
- }
-
- public void explicitlySelected(boolean acceptUnvalidated) {
- mNetworkAgent.explicitlySelected(acceptUnvalidated);
- }
-
- public void addCapability(int capability) {
- mNetworkCapabilities.addCapability(capability);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void removeCapability(int capability) {
- mNetworkCapabilities.removeCapability(capability);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setUids(Set<UidRange> uids) {
- mNetworkCapabilities.setUids(uids);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setSignalStrength(int signalStrength) {
- mNetworkCapabilities.setSignalStrength(signalStrength);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
- mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
-
- public void setNetworkCapabilities(NetworkCapabilities nc,
- boolean sendToConnectivityService) {
- mNetworkCapabilities.set(nc);
- if (sendToConnectivityService) {
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
- }
- }
-
+ /**
+ * Connect without adding any internet capability.
+ */
public void connectWithoutInternet() {
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ super.connect();
}
/**
@@ -708,7 +690,7 @@
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
- connect(validated, true);
+ connect(validated, true, false /* isStrictMode */);
}
/**
@@ -716,24 +698,22 @@
* @param validated Indicate if network should pretend to be validated.
* @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
*/
- public void connect(boolean validated, boolean hasInternet) {
- assertEquals("MockNetworkAgents can only be connected once",
- mNetworkInfo.getDetailedState(), DetailedState.IDLE);
- assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+ public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
- NetworkCallback callback = null;
+ ConnectivityManager.NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
if (validated) {
- setNetworkValid();
+ setNetworkValid(isStrictMode);
NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+ .addTransportType(getNetworkCapabilities().getTransportTypes()[0])
.clearCapabilities()
.build();
- callback = new NetworkCallback() {
+ callback = new ConnectivityManager.NetworkCallback() {
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
if (network.equals(getNetwork()) &&
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
validatedCv.open();
}
}
@@ -749,15 +729,15 @@
if (validated) {
// Wait for network to validate.
waitFor(validatedCv);
- setNetworkInvalid();
+ setNetworkInvalid(isStrictMode);
}
if (callback != null) mCm.unregisterNetworkCallback(callback);
}
- public void connectWithCaptivePortal(String redirectUrl) {
- setNetworkPortal(redirectUrl);
- connect(false);
+ public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkPortal(redirectUrl, isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
public void connectWithPartialConnectivity() {
@@ -765,47 +745,86 @@
connect(false);
}
- public void suspend() {
- mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ public void connectWithPartialValidConnectivity(boolean isStrictMode) {
+ setNetworkPartialValid(isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
- public void resume() {
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ void setNetworkValid(boolean isStrictMode) {
+ mNmValidationResult = NETWORK_VALIDATION_RESULT_VALID;
+ mNmValidationRedirectUrl = null;
+ int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS;
+ if (isStrictMode) {
+ probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ // The probesCompleted equals to probesSucceeded for the case of valid network, so put
+ // the same value into two different parameter of the method.
+ setProbesStatus(probesSucceeded, probesSucceeded);
}
- public void disconnect() {
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ void setNetworkInvalid(boolean isStrictMode) {
+ mNmValidationResult = VALIDATION_RESULT_INVALID;
+ mNmValidationRedirectUrl = null;
+ int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+ | NETWORK_VALIDATION_PROBE_HTTP;
+ int probesSucceeded = 0;
+ // If the isStrictMode is true, it means the network is invalid when NetworkMonitor
+ // tried to validate the private DNS but failed.
+ if (isStrictMode) {
+ probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP;
+ probesSucceeded = probesCompleted;
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- public Network getNetwork() {
- return new Network(mNetworkAgent.netId);
+ void setNetworkPortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkInvalid(isStrictMode);
+ mNmValidationRedirectUrl = redirectUrl;
+ // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP
+ // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet.
+ int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP;
+ int probesSucceeded = VALIDATION_RESULT_INVALID;
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- public ConditionVariable getPreventReconnectReceived() {
- return mPreventReconnectReceived;
+ void setNetworkPartial() {
+ mNmValidationResult = NETWORK_VALIDATION_RESULT_PARTIAL;
+ mNmValidationRedirectUrl = null;
+ int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+ | NETWORK_VALIDATION_PROBE_FALLBACK;
+ int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK;
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- public ConditionVariable getDisconnectedCV() {
- return mDisconnected;
+ void setNetworkPartialValid(boolean isStrictMode) {
+ setNetworkPartial();
+ mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID;
+ int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+ | NETWORK_VALIDATION_PROBE_HTTP;
+ int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP;
+ // Suppose the partial network cannot pass the private DNS validation as well, so only
+ // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- public void sendLinkProperties(LinkProperties lp) {
- mNetworkAgent.sendLinkProperties(lp);
+ void setProbesStatus(int probesCompleted, int probesSucceeded) {
+ mProbesCompleted = probesCompleted;
+ mProbesSucceeded = probesSucceeded;
}
- public void setStartKeepaliveError(int error) {
- mStartKeepaliveError = error;
- }
-
- public void setStopKeepaliveError(int error) {
- mStopKeepaliveError = error;
- }
-
- public void setExpectedKeepaliveSlot(Integer slot) {
- mExpectedKeepaliveSlot = slot;
+ void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+ try {
+ mNmCallbacks.notifyCaptivePortalDataChanged(data);
+ } catch (RemoteException e) {
+ throw new AssertionError("This cannot happen", e);
+ }
}
public String waitForRedirectUrl() {
@@ -813,12 +832,19 @@
return mRedirectUrl;
}
- public NetworkAgent getNetworkAgent() {
- return mNetworkAgent;
+ public void expectDisconnected() {
+ expectDisconnected(TIMEOUT_MS);
}
- public NetworkCapabilities getNetworkCapabilities() {
- return mNetworkCapabilities;
+ public void expectPreventReconnectReceived() {
+ expectPreventReconnectReceived(TIMEOUT_MS);
+ }
+
+ void notifyDataStallSuspected() throws Exception {
+ final DataStallReportParcelable p = new DataStallReportParcelable();
+ p.detectionMethod = DATA_STALL_DETECTION_METHOD;
+ p.timestampMillis = DATA_STALL_TIMESTAMP;
+ mNmCallbacks.notifyDataStallSuspected(p);
}
}
@@ -997,15 +1023,19 @@
private boolean mConnected = false;
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
- private MockNetworkAgent mMockNetworkAgent;
+ private TestNetworkAgentWrapper mMockNetworkAgent;
+ private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
+
+ private VpnInfo mVpnInfo;
+ private Network[] mUnderlyingNetworks;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
- userId);
+ userId, mock(KeyStore.class));
}
- public void setNetworkAgent(MockNetworkAgent agent) {
- waitForIdle(agent, TIMEOUT_MS);
+ public void setNetworkAgent(TestNetworkAgentWrapper agent) {
+ agent.waitForIdle(TIMEOUT_MS);
mMockNetworkAgent = agent;
mNetworkAgent = agent.getNetworkAgent();
mNetworkCapabilities.set(agent.getNetworkCapabilities());
@@ -1016,6 +1046,10 @@
updateCapabilities(null /* defaultNetwork */);
}
+ public void setVpnType(int vpnType) {
+ mVpnType = vpnType;
+ }
+
@Override
public int getNetId() {
if (mMockNetworkAgent == null) {
@@ -1034,6 +1068,11 @@
return mConnected; // Similar trickery
}
+ @Override
+ public int getActiveAppVpnType() {
+ return mVpnType;
+ }
+
private void connect(boolean isAlwaysMetered) {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
@@ -1070,194 +1109,70 @@
mConnected = false;
mConfig = null;
}
- }
- private class FakeWakeupMessage extends WakeupMessage {
- private static final int UNREASONABLY_LONG_WAIT = 1000;
+ @Override
+ public synchronized VpnInfo getVpnInfo() {
+ if (mVpnInfo != null) return mVpnInfo;
- public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
- super(context, handler, cmdName, cmd);
+ return super.getVpnInfo();
}
- public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
- int arg1, int arg2, Object obj) {
- super(context, handler, cmdName, cmd, arg1, arg2, obj);
+ private synchronized void setVpnInfo(VpnInfo vpnInfo) {
+ mVpnInfo = vpnInfo;
}
@Override
- public void schedule(long when) {
- long delayMs = when - SystemClock.elapsedRealtime();
- if (delayMs < 0) delayMs = 0;
- if (delayMs > UNREASONABLY_LONG_WAIT) {
- fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
- "ms into the future: " + delayMs);
- }
- Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
- mHandler.sendMessageDelayed(msg, delayMs);
+ public synchronized Network[] getUnderlyingNetworks() {
+ if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
+
+ return super.getUnderlyingNetworks();
}
- @Override
- public void cancel() {
- mHandler.removeMessages(mCmd, mObj);
- }
-
- @Override
- public void onAlarm() {
- throw new AssertionError("Should never happen. Update this fake.");
+ /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
+ private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
+ mUnderlyingNetworks = underlyingNetworks;
}
}
- private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
- public volatile boolean configRestrictsAvoidBadWifi;
- public volatile int configMeteredMultipathPreference;
+ private void mockVpn(int uid) {
+ synchronized (mService.mVpns) {
+ int userId = UserHandle.getUserId(uid);
+ mMockVpn = new MockVpn(userId);
+ // This has no effect unless the VPN is actually connected, because things like
+ // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
+ // netId, and check if that network is actually connected.
+ mService.mVpns.put(userId, mMockVpn);
+ }
+ }
- public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
+ private void setUidRulesChanged(int uidRules) throws RemoteException {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ }
+
+ private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ }
+
+ private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
+ return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+ }
+
+ private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker {
+ volatile boolean mConfigRestrictsAvoidBadWifi;
+ volatile int mConfigMeteredMultipathPreference;
+
+ WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) {
super(c, h, r);
}
@Override
public boolean configRestrictsAvoidBadWifi() {
- return configRestrictsAvoidBadWifi;
+ return mConfigRestrictsAvoidBadWifi;
}
@Override
public int configMeteredMultipathPreference() {
- return configMeteredMultipathPreference;
- }
- }
-
- private class WrappedConnectivityService extends ConnectivityService {
- public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
- private MockableSystemProperties mSystemProperties;
-
- public WrappedConnectivityService(Context context, INetworkManagementService netManager,
- INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
- super(context, netManager, statsService, policyManager, dnsResolver, log, netd);
- mNetd = netd;
- mLingerDelayMs = TEST_LINGER_DELAY_MS;
- }
-
- @Override
- protected MockableSystemProperties getSystemProperties() {
- // Minimal approach to overriding system properties: let most calls fall through to real
- // device values, and only override ones values that are important to this test.
- mSystemProperties = spy(new MockableSystemProperties());
- when(mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
- when(mSystemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
- return mSystemProperties;
- }
-
- @Override
- protected Tethering makeTethering() {
- return mock(Tethering.class);
- }
-
- @Override
- protected ProxyTracker makeProxyTracker() {
- return mock(ProxyTracker.class);
- }
-
- @Override
- protected int reserveNetId() {
- while (true) {
- final int netId = super.reserveNetId();
-
- // Don't overlap test NetIDs with real NetIDs as binding sockets to real networks
- // can have odd side-effects, like network validations succeeding.
- Context context = InstrumentationRegistry.getContext();
- final Network[] networks = ConnectivityManager.from(context).getAllNetworks();
- boolean overlaps = false;
- for (Network network : networks) {
- if (netId == network.netId) {
- overlaps = true;
- break;
- }
- }
- if (overlaps) continue;
-
- return netId;
- }
- }
-
- @Override
- protected boolean queryUserAccess(int uid, int netId) {
- return true;
- }
-
- public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
- return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
- }
-
- @Override
- public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
- Context c, Handler h, Runnable r) {
- final WrappedMultinetworkPolicyTracker tracker = new WrappedMultinetworkPolicyTracker(c, h, r);
- return tracker;
- }
-
- public WrappedMultinetworkPolicyTracker getMultinetworkPolicyTracker() {
- return (WrappedMultinetworkPolicyTracker) mMultinetworkPolicyTracker;
- }
-
- @Override
- protected NetworkStackClient getNetworkStack() {
- return mNetworkStack;
- }
-
- @Override
- public WakeupMessage makeWakeupMessage(
- Context context, Handler handler, String cmdName, int cmd, Object obj) {
- return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
- }
-
- @Override
- public boolean hasService(String name) {
- // Currenty, the only relevant service that ConnectivityService checks for is
- // ETHERNET_SERVICE.
- return Context.ETHERNET_SERVICE.equals(name);
- }
-
- @Override
- protected IpConnectivityMetrics.Logger metricsLogger() {
- return mMetricsService;
- }
-
- @Override
- protected void registerNetdEventCallback() {
- }
-
- public void mockVpn(int uid) {
- synchronized (mVpns) {
- int userId = UserHandle.getUserId(uid);
- mMockVpn = new MockVpn(userId);
- // This has no effect unless the VPN is actually connected, because things like
- // getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
- // netId, and check if that network is actually connected.
- mVpns.put(userId, mMockVpn);
- }
- }
-
- public void waitForIdle(int timeoutMs) {
- waitForIdleHandler(mHandlerThread, timeoutMs);
- }
-
- public void waitForIdle() {
- waitForIdle(TIMEOUT_MS);
- }
-
- public void setUidRulesChanged(int uidRules) {
- try {
- mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
- } catch (RemoteException ignored) {
- }
- }
-
- public void setRestrictBackgroundChanged(boolean restrictBackground) {
- try {
- mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
- } catch (RemoteException ignored) {
- }
+ return mConfigMeteredMultipathPreference;
}
}
@@ -1288,6 +1203,10 @@
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -1303,13 +1222,22 @@
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
- mService = new WrappedConnectivityService(mServiceContext,
+ mAlarmManagerThread = new HandlerThread("TestAlarmManager");
+ mAlarmManagerThread.start();
+ initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler());
+
+ mCsHandlerThread = new HandlerThread("TestConnectivityService");
+ final ConnectivityService.Dependencies deps = makeDependencies();
+ mService = new ConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
mNpm,
+ mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
- mMockDnsResolver);
+ deps);
+ mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ verify(deps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -1320,7 +1248,7 @@
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReady();
- mService.mockVpn(Process.myUid());
+ mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
// Ensure that the default setting for Captive Portals is used for most tests
@@ -1329,6 +1257,57 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
+ private ConnectivityService.Dependencies makeDependencies() {
+ final MockableSystemProperties systemProperties = spy(new MockableSystemProperties());
+ when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
+ when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
+
+ final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
+ doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
+ doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetworkStack).when(deps).getNetworkStack();
+ doReturn(systemProperties).when(deps).getSystemProperties();
+ doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
+ doReturn(mMetricsService).when(deps).getMetricsLogger();
+ doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
+ doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
+ doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
+ doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
+ doAnswer(inv -> {
+ mPolicyTracker = new WrappedMultinetworkPolicyTracker(
+ inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
+ return mPolicyTracker;
+ }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
+
+ return deps;
+ }
+
+ private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
+ doAnswer(inv -> {
+ final long when = inv.getArgument(1);
+ final WakeupMessage wakeupMsg = inv.getArgument(3);
+ final Handler handler = inv.getArgument(4);
+
+ long delayMs = when - SystemClock.elapsedRealtime();
+ if (delayMs < 0) delayMs = 0;
+ if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) {
+ fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS
+ + "ms into the future: " + delayMs);
+ }
+ alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */,
+ delayMs);
+
+ return null;
+ }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(),
+ any(WakeupMessage.class), any());
+
+ doAnswer(inv -> {
+ final WakeupMessage wakeupMsg = inv.getArgument(0);
+ alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */);
+ return null;
+ }).when(am).cancel(any(WakeupMessage.class));
+ }
+
@After
public void tearDown() throws Exception {
setAlwaysOnNetworks(false);
@@ -1345,6 +1324,9 @@
mEthernetNetworkAgent = null;
}
FakeSettingsProvider.clearSettingsProvider();
+
+ mCsHandlerThread.quitSafely();
+ mAlarmManagerThread.quitSafely();
}
private void mockDefaultPackages() throws Exception {
@@ -1364,21 +1346,6 @@
}));
}
- private static int transportToLegacyType(int transport) {
- switch (transport) {
- case TRANSPORT_ETHERNET:
- return TYPE_ETHERNET;
- case TRANSPORT_WIFI:
- return TYPE_WIFI;
- case TRANSPORT_CELLULAR:
- return TYPE_MOBILE;
- case TRANSPORT_VPN:
- return TYPE_VPN;
- default:
- return TYPE_NONE;
- }
- }
-
private void verifyActiveNetwork(int transport) {
// Test getActiveNetworkInfo()
assertNotNull(mCm.getActiveNetworkInfo());
@@ -1423,17 +1390,25 @@
* Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
* broadcasts are received.
*/
- private ConditionVariable waitForConnectivityBroadcasts(final int count) {
+ private ConditionVariable registerConnectivityBroadcast(final int count) {
+ return registerConnectivityBroadcastThat(count, intent -> true);
+ }
+
+ private ConditionVariable registerConnectivityBroadcastThat(final int count,
+ @NonNull final Predicate<Intent> filter) {
final ConditionVariable cv = new ConditionVariable();
- mServiceContext.registerReceiver(new BroadcastReceiver() {
+ final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
private int remaining = count;
public void onReceive(Context context, Intent intent) {
+ if (!filter.test(intent)) return;
if (--remaining == 0) {
cv.open();
mServiceContext.unregisterReceiver(this);
}
}
- }, new IntentFilter(CONNECTIVITY_ACTION));
+ };
+ mServiceContext.registerReceiver(receiver, intentFilter);
return cv;
}
@@ -1454,14 +1429,55 @@
}
@Test
+ public void testNetworkFeature() throws Exception {
+ // Connect the cell agent and wait for the connected broadcast.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL);
+ final ConditionVariable cv1 = registerConnectivityBroadcastThat(1,
+ intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE);
+ mCellNetworkAgent.connect(true);
+ waitFor(cv1);
+
+ // Build legacy request for SUPL.
+ final NetworkCapabilities legacyCaps = new NetworkCapabilities();
+ legacyCaps.addTransportType(TRANSPORT_CELLULAR);
+ legacyCaps.addCapability(NET_CAPABILITY_SUPL);
+ final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL,
+ ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
+
+ // File request, withdraw it and make sure no broadcast is sent
+ final ConditionVariable cv2 = registerConnectivityBroadcast(1);
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.requestNetwork(legacyRequest, callback);
+ callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ mCm.unregisterNetworkCallback(callback);
+ assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
+ // As the broadcast did not fire, the receiver was not unregistered. Do this now.
+ mServiceContext.clearRegisteredReceivers();
+
+ // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
+ // check that has been sent.
+ final AtomicBoolean vanillaAction = new AtomicBoolean(false);
+ final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
+ if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
+ vanillaAction.set(true);
+ }
+ return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
+ });
+ mCellNetworkAgent.disconnect();
+ waitFor(cv3);
+ assertTrue(vanillaAction.get());
+ }
+
+ @Test
public void testLingering() throws Exception {
verifyNoNetwork();
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
// Test bringing up validated cellular.
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
@@ -1471,7 +1487,7 @@
assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
// Test bringing up validated WiFi.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1481,14 +1497,14 @@
assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
// Test cellular linger timeout.
- waitFor(mCellNetworkAgent.getDisconnectedCV());
+ mCellNetworkAgent.expectDisconnected();
waitForIdle();
assertLength(1, mCm.getAllNetworks());
verifyActiveNetwork(TRANSPORT_WIFI);
assertLength(1, mCm.getAllNetworks());
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
// Test WiFi disconnect.
- cv = waitForConnectivityBroadcasts(1);
+ cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
waitFor(cv);
verifyNoNetwork();
@@ -1497,13 +1513,13 @@
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1512,18 +1528,18 @@
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ cv = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = waitForConnectivityBroadcasts(1);
+ cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
waitFor(cv);
verifyNoNetwork();
@@ -1532,24 +1548,24 @@
@Test
public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
// Test bringing up unvalidated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = waitForConnectivityBroadcasts(1);
+ cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
waitFor(cv);
verifyNoNetwork();
@@ -1558,23 +1574,23 @@
@Test
public void testUnlingeringDoesNotValidate() throws Exception {
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ cv = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test cellular disconnect.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1586,24 +1602,24 @@
@Test
public void testCellularOutscoresWeakWifi() throws Exception {
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi getting really weak.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(-11);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test WiFi restoring signal strength.
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(11);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1613,51 +1629,47 @@
public void testReapingNetwork() throws Exception {
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mWiFiNetworkAgent.expectDisconnected();
// Test bringing up cellular without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = mCellNetworkAgent.getDisconnectedCV();
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ final ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular.
// Expect it to be torn down because it could never be the highest scoring network
// satisfying the default request even if it validated.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- cv = mCellNetworkAgent.getDisconnectedCV();
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_WIFI);
- cv = mWiFiNetworkAgent.getDisconnectedCV();
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ mWiFiNetworkAgent.expectDisconnected();
}
@Test
public void testCellularFallback() throws Exception {
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Reevaluate WiFi (it'll instantly fail DNS).
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork());
@@ -1667,7 +1679,7 @@
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
@@ -1683,19 +1695,19 @@
@Test
public void testWiFiFallback() throws Exception {
// Test bringing up unvalidated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ cv = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
@@ -1712,274 +1724,37 @@
mCm.getDefaultRequest().networkCapabilities));
}
- enum CallbackState {
- NONE,
- AVAILABLE,
- NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- SUSPENDED,
- RESUMED,
- LOSING,
- LOST,
- UNAVAILABLE,
- BLOCKED_STATUS
- }
-
- private static class CallbackInfo {
- public final CallbackState state;
- public final Network network;
- public final Object arg;
- public CallbackInfo(CallbackState s, Network n, Object o) {
- state = s; network = n; arg = o;
- }
- public String toString() {
- return String.format("%s (%s) (%s)", state, network, arg);
- }
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof CallbackInfo)) return false;
- // Ignore timeMs, since it's unpredictable.
- CallbackInfo other = (CallbackInfo) o;
- return (state == other.state) && Objects.equals(network, other.network);
- }
- @Override
- public int hashCode() {
- return Objects.hash(state, network);
- }
- }
-
/**
* Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks
* this class receives, by calling expectCallback() exactly once each time a callback is
* received. assertNoCallback may be called at any time.
*/
- private class TestNetworkCallback extends NetworkCallback {
- private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
- private Network mLastAvailableNetwork;
-
- protected void setLastCallback(CallbackState state, Network network, Object o) {
- mCallbacks.offer(new CallbackInfo(state, network, o));
+ private class TestNetworkCallback extends TestableNetworkCallback {
+ TestNetworkCallback() {
+ super(TEST_CALLBACK_TIMEOUT_MS);
}
@Override
- public void onAvailable(Network network) {
- mLastAvailableNetwork = network;
- setLastCallback(CallbackState.AVAILABLE, network, null);
+ public void assertNoCallback() {
+ // TODO: better support this use case in TestableNetworkCallback
+ waitForIdle();
+ assertNoCallback(0 /* timeout */);
}
@Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
- setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
- }
-
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
- setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
- }
-
- @Override
- public void onUnavailable() {
- setLastCallback(CallbackState.UNAVAILABLE, null, null);
- }
-
- @Override
- public void onNetworkSuspended(Network network) {
- setLastCallback(CallbackState.SUSPENDED, network, null);
- }
-
- @Override
- public void onNetworkResumed(Network network) {
- setLastCallback(CallbackState.RESUMED, network, null);
- }
-
- @Override
- public void onLosing(Network network, int maxMsToLive) {
- setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
- }
-
- @Override
- public void onLost(Network network) {
- mLastAvailableNetwork = null;
- setLastCallback(CallbackState.LOST, network, null);
- }
-
- @Override
- public void onBlockedStatusChanged(Network network, boolean blocked) {
- setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
- }
-
- public Network getLastAvailableNetwork() {
- return mLastAvailableNetwork;
- }
-
- CallbackInfo nextCallback(int timeoutMs) {
- CallbackInfo cb = null;
- try {
- cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- }
- if (cb == null) {
- // LinkedBlockingQueue.poll() returns null if it timeouts.
- fail("Did not receive callback after " + timeoutMs + "ms");
- }
- return cb;
- }
-
- CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs) {
- final Network expectedNetwork = (agent != null) ? agent.getNetwork() : null;
- CallbackInfo expected = new CallbackInfo(state, expectedNetwork, 0);
- CallbackInfo actual = nextCallback(timeoutMs);
- assertEquals("Unexpected callback:", expected, actual);
-
- if (state == CallbackState.LOSING) {
+ public <T extends CallbackEntry> T expectCallback(final KClass<T> type, final HasNetwork n,
+ final long timeoutMs) {
+ final T callback = super.expectCallback(type, n, timeoutMs);
+ if (callback instanceof CallbackEntry.Losing) {
+ // TODO : move this to the specific test(s) needing this rather than here.
+ final CallbackEntry.Losing losing = (CallbackEntry.Losing) callback;
+ final int maxMsToLive = losing.getMaxMsToLive();
String msg = String.format(
"Invalid linger time value %d, must be between %d and %d",
- actual.arg, 0, mService.mLingerDelayMs);
- int maxMsToLive = (Integer) actual.arg;
+ maxMsToLive, 0, mService.mLingerDelayMs);
assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs);
}
-
- return actual;
- }
-
- CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
- return expectCallback(state, agent, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
- return expectCallbackLike(fn, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
- int timeLeft = timeoutMs;
- while (timeLeft > 0) {
- long start = SystemClock.elapsedRealtime();
- CallbackInfo info = nextCallback(timeLeft);
- if (fn.test(info)) {
- return info;
- }
- timeLeft -= (SystemClock.elapsedRealtime() - start);
- }
- fail("Did not receive expected callback after " + timeoutMs + "ms");
- return null;
- }
-
- // Expects onAvailable and the callbacks that follow it. These are:
- // - onSuspended, iff the network was suspended when the callbacks fire.
- // - onCapabilitiesChanged.
- // - onLinkPropertiesChanged.
- // - onBlockedStatusChanged.
- //
- // @param agent the network to expect the callbacks on.
- // @param expectSuspended whether to expect a SUSPENDED callback.
- // @param expectValidated the expected value of the VALIDATED capability in the
- // onCapabilitiesChanged callback.
- // @param timeoutMs how long to wait for the callbacks.
- void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
- boolean expectValidated, boolean expectBlocked, int timeoutMs) {
- expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
- if (expectSuspended) {
- expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
- }
- if (expectValidated) {
- expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
- } else {
- expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
- }
- expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
- expectBlockedStatusCallback(expectBlocked, agent);
- }
-
- // Expects the available callbacks (validated), plus onSuspended.
- void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
- expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- // Expects the available callbacks (where the onCapabilitiesChanged must contain the
- // VALIDATED capability), plus another onCapabilitiesChanged which is identical to the
- // one we just sent.
- // TODO: this is likely a bug. Fix it and remove this method.
- void expectAvailableDoubleValidatedCallbacks(MockNetworkAgent agent) {
- expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
- NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
- expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
- // Implicitly check the network is allowed to use.
- // TODO: should we need to consider if network is in blocked status in this case?
- expectBlockedStatusCallback(false, agent);
- NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
- assertEquals(nc1, nc2);
- }
-
- // Expects the available callbacks where the onCapabilitiesChanged must not have validated,
- // then expects another onCapabilitiesChanged that has the validated bit set. This is used
- // when a network connects and satisfies a callback, and then immediately validates.
- void expectAvailableThenValidatedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacksUnvalidated(agent);
- expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
- }
-
- NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
- return expectCapabilitiesWith(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- NetworkCapabilities expectCapabilitiesWith(int capability, MockNetworkAgent agent,
- int timeoutMs) {
- CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
- NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
- assertTrue(nc.hasCapability(capability));
- return nc;
- }
-
- NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
- return expectCapabilitiesWithout(capability, agent, TEST_CALLBACK_TIMEOUT_MS);
- }
-
- NetworkCapabilities expectCapabilitiesWithout(int capability, MockNetworkAgent agent,
- int timeoutMs) {
- CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
- NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
- assertFalse(nc.hasCapability(capability));
- return nc;
- }
-
- void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
- CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
- assertTrue("Received capabilities don't match expectations : " + cbi.arg,
- fn.test((NetworkCapabilities) cbi.arg));
- }
-
- void expectLinkPropertiesLike(Predicate<LinkProperties> fn, MockNetworkAgent agent) {
- CallbackInfo cbi = expectCallback(CallbackState.LINK_PROPERTIES, agent);
- assertTrue("Received LinkProperties don't match expectations : " + cbi.arg,
- fn.test((LinkProperties) cbi.arg));
- }
-
- void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
- CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
- boolean actualBlocked = (boolean) cbi.arg;
- assertEquals(expectBlocked, actualBlocked);
- }
-
- void assertNoCallback() {
- waitForIdle();
- CallbackInfo c = mCallbacks.peek();
- assertNull("Unexpected callback: " + c, c);
+ return callback;
}
}
@@ -2007,8 +1782,8 @@
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
// Test unvalidated networks
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -2022,8 +1797,8 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cv = waitForConnectivityBroadcasts(2);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ cv = registerConnectivityBroadcast(2);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2031,23 +1806,23 @@
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = waitForConnectivityBroadcasts(2);
+ cv = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cellNetworkCallback.assertNoCallback();
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = waitForConnectivityBroadcasts(1);
+ cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// Test validated networks
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -2060,29 +1835,92 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
mCellNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
}
+ private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, callback);
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ final LinkProperties newLp = new LinkProperties();
+ final Uri capportUrl = Uri.parse("https://capport.example.com/api");
+ final CaptivePortalData capportData = new CaptivePortalData.Builder()
+ .setCaptive(true).build();
+
+ final Uri expectedCapportUrl = sanitized ? null : capportUrl;
+ newLp.setCaptivePortalApiUrl(capportUrl);
+ mWiFiNetworkAgent.sendLinkProperties(newLp);
+ callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
+ defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
+
+ final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
+ mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData);
+ callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+ defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+
+ final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
+ assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl());
+ assertEquals(expectedCapportData, lp.getCaptivePortalData());
+ }
+
@Test
- public void testMultipleLingering() {
+ public void networkCallbacksSanitizationTest_Sanitize() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_DENIED);
+ doNetworkCallbacksSanitizationTest(true /* sanitized */);
+ }
+
+ @Test
+ public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_GRANTED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
+ doNetworkCallbacksSanitizationTest(false /* sanitized */);
+ }
+
+ @Test
+ public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+ doNetworkCallbacksSanitizationTest(false /* sanitized */);
+ }
+
+ @Test
+ public void testMultipleLingering() throws Exception {
// This test would be flaky with the default 120ms timer: that is short enough that
// lingered networks are torn down before assertions can be run. We don't want to mock the
// lingering timer to keep the WakeupMessage logic realistic: this has already proven useful
@@ -2098,9 +1936,9 @@
TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -2117,7 +1955,7 @@
// We then get LOSING when wifi validates and cell is outscored.
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -2126,20 +1964,20 @@
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
for (int i = 0; i < 4; i++) {
- MockNetworkAgent oldNetwork, newNetwork;
+ TestNetworkAgentWrapper oldNetwork, newNetwork;
if (i % 2 == 0) {
mWiFiNetworkAgent.adjustScore(-15);
oldNetwork = mWiFiNetworkAgent;
@@ -2150,7 +1988,7 @@
newNetwork = mWiFiNetworkAgent;
}
- callback.expectCallback(CallbackState.LOSING, oldNetwork);
+ callback.expectCallback(CallbackEntry.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
defaultCallback.expectAvailableCallbacksValidated(newNetwork);
@@ -2164,7 +2002,7 @@
// We expect a notification about the capabilities change, and nothing else.
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
defaultCallback.assertNoCallback();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Wifi no longer satisfies our listen, which is for an unmetered network.
@@ -2173,11 +2011,11 @@
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
@@ -2191,7 +2029,7 @@
mCm.registerNetworkCallback(request, callback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
@@ -2200,7 +2038,7 @@
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2208,65 +2046,65 @@
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
// Cell is lingered because it would not satisfy any request, even if it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
// If a network is lingering, and we add and remove a request from it, resume lingering.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -2277,13 +2115,13 @@
// TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
// lingering?
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Similar to the above: lingering can start even after the lingered request is removed.
// Disconnect wifi and switch to cell.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -2292,7 +2130,7 @@
mCm.requestNetwork(cellRequest, noopCallback);
// Now connect wifi, and expect it to become the default network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -2302,34 +2140,34 @@
callback.assertNoCallback();
// Now unregister cellRequest and expect cell to start lingering.
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Let linger run its course.
callback.assertNoCallback();
final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, lingerTimeoutMs);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs);
// Register a TRACK_DEFAULT request and check that it does not affect lingering.
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(trackDefaultCallback);
trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Let linger run its course.
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
// Clean up.
mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- trackDefaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ trackDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -2356,8 +2194,8 @@
TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -2367,7 +2205,7 @@
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
// File a request for cellular, then release it.
@@ -2376,7 +2214,7 @@
NetworkCallback noopCallback = new NetworkCallback();
mCm.requestNetwork(cellRequest, noopCallback);
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Let linger run its course.
callback.assertNoCallback();
@@ -2390,7 +2228,7 @@
}
@Test
- public void testExplicitlySelected() {
+ public void testExplicitlySelected() throws Exception {
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
.build();
@@ -2398,13 +2236,13 @@
mCm.registerNetworkCallback(request, callback);
// Bring up validated cell.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up unvalidated wifi with explicitlySelected=true.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.explicitlySelected(false);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2420,47 +2258,69 @@
// If the user chooses yes on the "No Internet access, stay connected?" dialog, we switch to
// wifi even though it's unvalidated.
mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), true, false);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Disconnect wifi, and then reconnect, again with explicitlySelected=true.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.explicitlySelected(false);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
// network to disconnect.
mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), false, false);
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Reconnect, again with explicitlySelected=true, but this time validate.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.explicitlySelected(false);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// BUG: the network will no longer linger, even though it's validated and outscored.
// TODO: fix this.
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
+ // Disconnect wifi, and then reconnect as if the user had selected "yes, don't ask again"
+ // (i.e., with explicitlySelected=true and acceptUnvalidated=true). Expect to switch to
+ // wifi immediately.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(true, true);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mEthernetNetworkAgent);
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ mEthernetNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+
+ // Disconnect and reconnect with explicitlySelected=false and acceptUnvalidated=true.
+ // Check that the network is not scored specially and that the device prefers cell data.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(false, true);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
// Clean up.
mWiFiNetworkAgent.disconnect();
mCellNetworkAgent.disconnect();
- mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
private int[] makeIntArray(final int size, final int value) {
@@ -2511,7 +2371,7 @@
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
- MockNetworkAgent testAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
// Rather than create a validated network which complicates things by registering it's
// own NetworkRequest during startup, just bump up the score to cancel out the
// unvalidated penalty.
@@ -2560,7 +2420,7 @@
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
- testFactory.unregister();
+ testFactory.terminate();
if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback);
handlerThread.quit();
}
@@ -2586,6 +2446,38 @@
}
@Test
+ public void testNetworkFactoryUnregister() throws Exception {
+ final NetworkCapabilities filter = new NetworkCapabilities();
+ filter.clearAll();
+
+ final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+ handlerThread.start();
+
+ // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it
+ // does not crash.
+ for (int i = 0; i < 100; i++) {
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter);
+ // Register the factory and don't be surprised when the default request arrives.
+ testFactory.expectAddRequestsWithScores(0);
+ testFactory.register();
+ testFactory.waitForNetworkRequests(1);
+
+ testFactory.setScoreFilter(42);
+ testFactory.terminate();
+
+ if (i % 2 == 0) {
+ try {
+ testFactory.register();
+ fail("Re-registering terminated NetworkFactory should throw");
+ } catch (IllegalStateException expected) {
+ }
+ }
+ }
+ handlerThread.quit();
+ }
+
+ @Test
public void testNoMutableNetworkRequests() throws Exception {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
NetworkRequest request1 = new NetworkRequest.Builder()
@@ -2596,27 +2488,26 @@
.build();
Class<IllegalArgumentException> expected = IllegalArgumentException.class;
- assertException(() -> { mCm.requestNetwork(request1, new NetworkCallback()); }, expected);
- assertException(() -> { mCm.requestNetwork(request1, pendingIntent); }, expected);
- assertException(() -> { mCm.requestNetwork(request2, new NetworkCallback()); }, expected);
- assertException(() -> { mCm.requestNetwork(request2, pendingIntent); }, expected);
+ assertThrows(expected, () -> mCm.requestNetwork(request1, new NetworkCallback()));
+ assertThrows(expected, () -> mCm.requestNetwork(request1, pendingIntent));
+ assertThrows(expected, () -> mCm.requestNetwork(request2, new NetworkCallback()));
+ assertThrows(expected, () -> mCm.requestNetwork(request2, pendingIntent));
}
@Test
public void testMMSonWiFi() throws Exception {
// Test bringing up cellular without MMS NetworkRequest gets reaped
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
- ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV();
mCellNetworkAgent.connectWithoutInternet();
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
waitForIdle();
assertEmpty(mCm.getAllNetworks());
verifyNoNetwork();
// Test bringing up validated WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ final ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -2628,24 +2519,23 @@
mCm.requestNetwork(builder.build(), networkCallback);
// Test bringing up unvalidated cellular with MMS
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
- cv = mCellNetworkAgent.getDisconnectedCV();
mCm.unregisterNetworkCallback(networkCallback);
- waitFor(cv);
+ mCellNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_WIFI);
}
@Test
public void testMMSonCell() throws Exception {
// Test bringing up cellular without MMS
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(false);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
@@ -2657,21 +2547,21 @@
mCm.requestNetwork(builder.build(), networkCallback);
// Test bringing up MMS cellular network
- MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ TestNetworkAgentWrapper
+ mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
- cv = mmsNetworkAgent.getDisconnectedCV();
mCm.unregisterNetworkCallback(networkCallback);
- waitFor(cv);
+ mmsNetworkAgent.expectDisconnected();
verifyActiveNetwork(TRANSPORT_CELLULAR);
}
@Test
- public void testPartialConnectivity() {
+ public void testPartialConnectivity() throws Exception {
// Register network callback.
NetworkRequest request = new NetworkRequest.Builder()
.clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -2680,12 +2570,12 @@
mCm.registerNetworkCallback(request, callback);
// Bring up validated mobile data.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi with partial connectivity.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2696,7 +2586,7 @@
// With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
// probe.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
// If the user chooses yes to use this partial connectivity wifi, switch the default
// network to wifi and check if wifi becomes valid or not.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
@@ -2704,15 +2594,12 @@
// If user accepts partial connectivity network,
// NetworkMonitor#setAcceptPartialConnectivity() should be called too.
waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
+
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED,
mWiFiNetworkAgent);
assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
@@ -2720,8 +2607,8 @@
// Disconnect and reconnect wifi with partial connectivity again.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2732,69 +2619,76 @@
// If the user chooses no, disconnect wifi immediately.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */,
false /* always */);
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// If user accepted partial connectivity before, and device reconnects to that network
// again, but now the network has full connectivity. The network shouldn't contain
// NET_CAPABILITY_PARTIAL_CONNECTIVITY.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// acceptUnvalidated is also used as setting for accepting partial networks.
- mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
+ mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
+ true /* acceptUnvalidated */);
mWiFiNetworkAgent.connect(true);
+
// If user accepted partial connectivity network before,
// NetworkMonitor#setAcceptPartialConnectivity() will be called in
// ConnectivityService#updateNetworkInfo().
- waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
+
// Wifi should be the default network.
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- // If user accepted partial connectivity before, and now the device reconnects to the
- // partial connectivity network. The network should be valid and contain
- // NET_CAPABILITY_PARTIAL_CONNECTIVITY.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
- // Current design cannot send multi-testResult from NetworkMonitor to ConnectivityService.
- // So, if user accepts partial connectivity, NetworkMonitor will send PARTIAL_CONNECTIVITY
- // to ConnectivityService first then send VALID. Once NetworkMonitor support
- // multi-testResult, this test case also need to be changed to meet the new design.
+ // The user accepted partial connectivity and selected "don't ask again". Now the user
+ // reconnects to the partial connectivity network. Switch to wifi as soon as partial
+ // connectivity is detected.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */,
+ true /* acceptUnvalidated */);
mWiFiNetworkAgent.connectWithPartialConnectivity();
// If user accepted partial connectivity network before,
// NetworkMonitor#setAcceptPartialConnectivity() will be called in
// ConnectivityService#updateNetworkInfo().
- waitForIdle();
- try {
- verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- fail(e.getMessage());
- }
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- // TODO: If the user accepted partial connectivity, we shouldn't switch to wifi until
- // NetworkMonitor detects partial connectivity
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
+
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // If the user accepted partial connectivity, and the device auto-reconnects to the partial
+ // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */,
+ true /* acceptUnvalidated */);
+
+ // NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as
+ // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls
+ // notifyNetworkConnected.
+ mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
+ callback.expectCapabilitiesWith(
+ NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
}
@Test
- public void testCaptivePortalOnPartialConnectivity() throws RemoteException {
+ public void testCaptivePortalOnPartialConnectivity() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2807,9 +2701,9 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String redirectUrl = "http://android.com/path";
- mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl);
@@ -2827,12 +2721,12 @@
mWiFiNetworkAgent);
// Report partial connectivity is accepted.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
false /* always */);
waitForIdle();
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
NetworkCapabilities nc =
validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
@@ -2843,7 +2737,7 @@
}
@Test
- public void testCaptivePortal() {
+ public void testCaptivePortal() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2856,46 +2750,43 @@
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
// Expect onLost callback.
mWiFiNetworkAgent.disconnect();
- captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- // Expect no notification to be shown when captive portal disappears by itself
- verify(mNotificationManager, never()).notifyAsUser(
- anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any());
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
- validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
}
@Test
- public void testCaptivePortalApp() throws RemoteException {
+ public void testCaptivePortalApp() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2907,7 +2798,7 @@
mCm.registerNetworkCallback(validatedRequest, validatedCallback);
// Bring up wifi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
@@ -2920,10 +2811,10 @@
mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
// Turn into a captive portal.
- mWiFiNetworkAgent.setNetworkPortal("http://example.com");
+ mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
mCm.startCaptivePortalApp(wifiNetwork);
@@ -2935,25 +2826,25 @@
final String testKey = "testkey";
final String testValue = "testvalue";
testBundle.putString(testKey, testValue);
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_GRANTED);
mCm.startCaptivePortalApp(wifiNetwork, testBundle);
final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction());
assertEquals(testValue, signInIntent.getStringExtra(testKey));
// Report that the captive portal is dismissed, and check that callbacks are fired
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- verify(mNotificationManager, times(1)).notifyAsUser(anyString(),
- eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL));
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(validatedCallback);
mCm.unregisterNetworkCallback(captivePortalCallback);
}
@Test
- public void testAvoidOrIgnoreCaptivePortals() {
+ public void testAvoidOrIgnoreCaptivePortals() throws Exception {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
@@ -2967,18 +2858,50 @@
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_AVOID);
// Bring up a network with a captive portal.
// Expect it to fail to connect and not result in any callbacks.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- ConditionVariable disconnectCv = mWiFiNetworkAgent.getDisconnectedCV();
- ConditionVariable avoidCv = mWiFiNetworkAgent.getPreventReconnectReceived();
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- waitFor(disconnectCv);
- waitFor(avoidCv);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
+ mWiFiNetworkAgent.expectDisconnected();
+ mWiFiNetworkAgent.expectPreventReconnectReceived();
assertNoCallbacks(captivePortalCallback, validatedCallback);
}
+ @Test
+ public void testCaptivePortalApi() throws Exception {
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
+ final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
+ mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ final String redirectUrl = "http://example.com/firstPath";
+
+ mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ final CaptivePortalData testData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(redirectUrl))
+ .setBytesRemaining(12345L)
+ .build();
+
+ mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData);
+
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> testData.equals(lp.getCaptivePortalData()));
+
+ final LinkProperties newLps = new LinkProperties();
+ newLps.setMtu(1234);
+ mWiFiNetworkAgent.sendLinkProperties(newLps);
+ // CaptivePortalData is not lost and unchanged when LPs are received from the NetworkAgent
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234);
+ }
+
private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
}
@@ -2993,12 +2916,12 @@
* does work.
*/
@Test
- public void testNetworkSpecifier() {
+ public void testNetworkSpecifier() throws Exception {
// A NetworkSpecifier subclass that matches all networks but must not be visible to apps.
class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
Parcelable {
@Override
- public boolean satisfiedBy(NetworkSpecifier other) {
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
return true;
}
@@ -3026,7 +2949,7 @@
}
@Override
- public boolean satisfiedBy(NetworkSpecifier other) {
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
if (other instanceof LocalStringNetworkSpecifier) {
return TextUtils.equals(mString,
((LocalStringNetworkSpecifier) other).mString);
@@ -3073,7 +2996,7 @@
LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo");
LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar");
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -3084,24 +3007,24 @@
mWiFiNetworkAgent.setNetworkSpecifier(nsFoo);
cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo),
- mWiFiNetworkAgent);
+ c.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier().equals(nsFoo));
}
- cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsFoo),
- mWiFiNetworkAgent);
+ cFoo.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier().equals(nsFoo));
assertEquals(nsFoo,
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
cFoo.assertNoCallback();
mWiFiNetworkAgent.setNetworkSpecifier(nsBar);
- cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar),
- mWiFiNetworkAgent);
+ c.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier().equals(nsBar));
}
- cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier().equals(nsBar),
- mWiFiNetworkAgent);
+ cBar.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier().equals(nsBar));
assertEquals(nsBar,
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
cBar.assertNoCallback();
@@ -3109,23 +3032,23 @@
mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier());
cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c : emptyCallbacks) {
- c.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
- mWiFiNetworkAgent);
+ c.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier() == null);
}
- cFoo.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
- mWiFiNetworkAgent);
- cBar.expectCapabilitiesLike((caps) -> caps.getNetworkSpecifier() == null,
- mWiFiNetworkAgent);
+ cFoo.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier() == null);
+ cBar.expectCapabilitiesThat(mWiFiNetworkAgent,
+ (caps) -> caps.getNetworkSpecifier() == null);
assertNull(
mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier());
cFoo.assertNoCallback();
cBar.assertNoCallback();
mWiFiNetworkAgent.setNetworkSpecifier(null);
- cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- cBar.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ cBar.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ c.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
}
assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
@@ -3133,50 +3056,45 @@
@Test
public void testInvalidNetworkSpecifier() {
- try {
+ assertThrows(IllegalArgumentException.class, () -> {
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.setNetworkSpecifier(new MatchAllNetworkSpecifier());
- fail("NetworkRequest builder with MatchAllNetworkSpecifier");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ });
- try {
+ assertThrows(IllegalArgumentException.class, () -> {
NetworkCapabilities networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
- ConnectivityManager.TYPE_WIFI);
- fail("ConnectivityService requestNetwork with MatchAllNetworkSpecifier");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
+ });
class NonParcelableSpecifier extends NetworkSpecifier {
- public boolean satisfiedBy(NetworkSpecifier other) { return false; }
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ return false;
+ }
};
class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
@Override public int describeContents() { return 0; }
@Override public void writeToParcel(Parcel p, int flags) {}
}
- NetworkRequest.Builder builder;
- builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
- try {
+ final NetworkRequest.Builder builder =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ assertThrows(ClassCastException.class, () -> {
builder.setNetworkSpecifier(new NonParcelableSpecifier());
Parcel parcelW = Parcel.obtain();
builder.build().writeToParcel(parcelW, 0);
- fail("Parceling a non-parcelable specifier did not throw an exception");
- } catch (Exception e) {
- // expected
- }
+ });
- builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
- builder.setNetworkSpecifier(new ParcelableSpecifier());
- NetworkRequest nr = builder.build();
+ final NetworkRequest nr =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET)
+ .setNetworkSpecifier(new ParcelableSpecifier())
+ .build();
assertNotNull(nr);
- try {
+ assertThrows(BadParcelableException.class, () -> {
Parcel parcelW = Parcel.obtain();
nr.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
@@ -3186,44 +3104,19 @@
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR);
- fail("Unparceling a non-framework NetworkSpecifier did not throw an exception");
- } catch (Exception e) {
- // expected
- }
+ });
}
@Test
- public void testNetworkSpecifierUidSpoofSecurityException() {
- class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
- @Override
- public boolean satisfiedBy(NetworkSpecifier other) {
- return true;
- }
-
- @Override
- public void assertValidFromUid(int requestorUid) {
- throw new SecurityException("failure");
- }
-
- @Override
- public int describeContents() { return 0; }
- @Override
- public void writeToParcel(Parcel dest, int flags) {}
- }
-
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ public void testNetworkRequestUidSpoofSecurityException() throws Exception {
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
-
- UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
- NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
- networkSpecifier).build();
+ NetworkRequest networkRequest = newWifiRequestBuilder().build();
TestNetworkCallback networkCallback = new TestNetworkCallback();
- try {
+ doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
+ assertThrows(SecurityException.class, () -> {
mCm.requestNetwork(networkRequest, networkCallback);
- fail("Network request with spoofed UID did not throw a SecurityException");
- } catch (SecurityException e) {
- // expected
- }
+ });
}
@Test
@@ -3235,36 +3128,20 @@
.build();
// Registering a NetworkCallback with signal strength but w/o NETWORK_SIGNAL_STRENGTH_WAKEUP
// permission should get SecurityException.
- try {
- mCm.registerNetworkCallback(r, new NetworkCallback());
- fail("Expected SecurityException filing a callback with signal strength");
- } catch (SecurityException expected) {
- // expected
- }
+ assertThrows(SecurityException.class, () ->
+ mCm.registerNetworkCallback(r, new NetworkCallback()));
- try {
- mCm.registerNetworkCallback(r, PendingIntent.getService(
- mServiceContext, 0, new Intent(), 0));
- fail("Expected SecurityException filing a callback with signal strength");
- } catch (SecurityException expected) {
- // expected
- }
+ assertThrows(SecurityException.class, () ->
+ mCm.registerNetworkCallback(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0)));
// Requesting a Network with signal strength should get IllegalArgumentException.
- try {
- mCm.requestNetwork(r, new NetworkCallback());
- fail("Expected IllegalArgumentException filing a request with signal strength");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ assertThrows(IllegalArgumentException.class, () ->
+ mCm.requestNetwork(r, new NetworkCallback()));
- try {
- mCm.requestNetwork(r, PendingIntent.getService(
- mServiceContext, 0, new Intent(), 0));
- fail("Expected IllegalArgumentException filing a request with signal strength");
- } catch (IllegalArgumentException expected) {
- // expected
- }
+ assertThrows(IllegalArgumentException.class, () ->
+ mCm.requestNetwork(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0)));
}
@Test
@@ -3283,14 +3160,14 @@
cellNetworkCallback.assertNoCallback();
// Bring up cell and expect CALLBACK_AVAILABLE.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi and expect CALLBACK_AVAILABLE.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -3298,12 +3175,12 @@
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up cell. Expect no default network callback, since it won't be the default.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
@@ -3313,16 +3190,17 @@
// followed by AVAILABLE cell.
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
@@ -3333,7 +3211,7 @@
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
- defaultNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -3347,7 +3225,7 @@
mCm.requestNetwork(cellRequest, cellNetworkCallback);
// Bring up the mobile network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
// We should get onAvailable(), onCapabilitiesChanged(), and
@@ -3361,15 +3239,17 @@
lp.setInterfaceName("foonet_data0");
mCellNetworkAgent.sendLinkProperties(lp);
// We should get onLinkPropertiesChanged().
- cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
+ mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Suspend the network.
mCellNetworkAgent.suspend();
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
+ assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState());
// Register a garden variety default network request.
TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
@@ -3383,8 +3263,9 @@
mCellNetworkAgent.resume();
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.RESUMED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
+ assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState());
dfltNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
@@ -3416,7 +3297,7 @@
waitForIdle();
}
- private boolean isForegroundNetwork(MockNetworkAgent network) {
+ private boolean isForegroundNetwork(TestNetworkAgentWrapper network) {
NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
assertNotNull(nc);
return nc.hasCapability(NET_CAPABILITY_FOREGROUND);
@@ -3436,21 +3317,21 @@
mCm.registerNetworkCallback(request, callback);
mCm.registerNetworkCallback(fgRequest, fgCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -3458,7 +3339,7 @@
// When lingering is complete, cell is still there but is now in the background.
waitForIdle();
int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
- fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs);
+ fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs);
// Expect a network capabilities update sans FOREGROUND.
callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -3469,14 +3350,11 @@
.addTransportType(TRANSPORT_CELLULAR).build();
final TestNetworkCallback cellCallback = new TestNetworkCallback();
mCm.requestNetwork(cellRequest, cellCallback);
- // NOTE: This request causes the network's capabilities to change. This
- // is currently delivered before the onAvailable() callbacks.
- // TODO: Fix this.
- cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Expect a network capabilities update with FOREGROUND, because the most recent
// request causes its state to change.
+ cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -3484,7 +3362,7 @@
// Release the request. The network immediately goes into the background, since it was not
// lingering.
mCm.unregisterNetworkCallback(cellCallback);
- fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
// Expect a network capabilities update sans FOREGROUND.
callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -3492,8 +3370,8 @@
// Disconnect wifi and check that cell is foreground again.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -3529,20 +3407,20 @@
};
}
- assertTimeLimit("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> {
+ assertRunsInAtMost("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> {
for (NetworkCallback cb : callbacks) {
mCm.registerNetworkCallback(request, cb);
}
});
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
// Don't request that the network validate, because otherwise connect() will block until
// the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired,
// and we won't actually measure anything.
mCellNetworkAgent.connect(false);
long onAvailableDispatchingDuration = durationOf(() -> {
- awaitLatch(availableLatch, 10 * CONNECT_TIME_LIMIT_MS);
+ await(availableLatch, 10 * CONNECT_TIME_LIMIT_MS);
});
Log.d(TAG, String.format("Dispatched %d of %d onAvailable callbacks in %dms",
NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS,
@@ -3552,12 +3430,12 @@
onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS);
// Give wifi a high enough score that we'll linger cell when wifi comes up.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(40);
mWiFiNetworkAgent.connect(false);
long onLostDispatchingDuration = durationOf(() -> {
- awaitLatch(losingLatch, 10 * SWITCH_TIME_LIMIT_MS);
+ await(losingLatch, 10 * SWITCH_TIME_LIMIT_MS);
});
Log.d(TAG, String.format("Dispatched %d of %d onLosing callbacks in %dms",
NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, onLostDispatchingDuration));
@@ -3565,33 +3443,13 @@
NUM_REQUESTS, onLostDispatchingDuration, SWITCH_TIME_LIMIT_MS),
onLostDispatchingDuration <= SWITCH_TIME_LIMIT_MS);
- assertTimeLimit("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> {
+ assertRunsInAtMost("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> {
for (NetworkCallback cb : callbacks) {
mCm.unregisterNetworkCallback(cb);
}
});
}
- private long durationOf(Runnable fn) {
- long startTime = SystemClock.elapsedRealtime();
- fn.run();
- return SystemClock.elapsedRealtime() - startTime;
- }
-
- private void assertTimeLimit(String descr, long timeLimit, Runnable fn) {
- long timeTaken = durationOf(fn);
- String msg = String.format("%s: took %dms, limit was %dms", descr, timeTaken, timeLimit);
- Log.d(TAG, msg);
- assertTrue(msg, timeTaken <= timeLimit);
- }
-
- private boolean awaitLatch(CountDownLatch l, long timeoutMs) {
- try {
- return l.await(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {}
- return false;
- }
-
@Test
public void testMobileDataAlwaysOn() throws Exception {
grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
@@ -3616,7 +3474,7 @@
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
@@ -3633,7 +3491,7 @@
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
@@ -3651,10 +3509,10 @@
testFactory.waitForNetworkRequests(1);
// ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertLength(1, mCm.getAllNetworks());
- testFactory.unregister();
+ testFactory.terminate();
mCm.unregisterNetworkCallback(cellNetworkCallback);
handlerThread.quit();
}
@@ -3662,48 +3520,46 @@
@Test
public void testAvoidBadWifiSetting() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI;
- tracker.configRestrictsAvoidBadWifi = false;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
String[] values = new String[] {null, "0", "1"};
for (int i = 0; i < values.length; i++) {
Settings.Global.putInt(cr, settingName, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
String msg = String.format("config=false, setting=%s", values[i]);
assertTrue(mService.avoidBadWifi());
- assertFalse(msg, tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated());
}
- tracker.configRestrictsAvoidBadWifi = true;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
Settings.Global.putInt(cr, settingName, 0);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertFalse(mService.avoidBadWifi());
- assertFalse(tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
Settings.Global.putInt(cr, settingName, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertTrue(mService.avoidBadWifi());
- assertFalse(tracker.shouldNotifyWifiUnvalidated());
+ assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated());
Settings.Global.putString(cr, settingName, null);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
assertFalse(mService.avoidBadWifi());
- assertTrue(tracker.shouldNotifyWifiUnvalidated());
+ assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated());
}
@Test
public void testAvoidBadWifi() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
// Pretend we're on a carrier that restricts switching away from bad wifi.
- tracker.configRestrictsAvoidBadWifi = true;
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
// File a request for cell to ensure it doesn't go down.
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
@@ -3722,27 +3578,27 @@
mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 0);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
// Bring up validated cell.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Because avoid bad wifi is off, we don't switch to cellular.
defaultCallback.assertNoCallback();
@@ -3754,14 +3610,14 @@
// Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect
// that we switch back to cell.
- tracker.configRestrictsAvoidBadWifi = false;
- tracker.reevaluate();
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = false;
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
- tracker.configRestrictsAvoidBadWifi = true;
- tracker.reevaluate();
+ mPolicyTracker.mConfigRestrictsAvoidBadWifi = true;
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
@@ -3776,21 +3632,21 @@
// Disconnect and reconnect wifi to clear the one-time switch above.
mWiFiNetworkAgent.disconnect();
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Simulate the user selecting "switch" and checking the don't ask again checkbox.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
// We now switch to cell.
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
@@ -3803,17 +3659,17 @@
// Simulate the user turning the cellular fallback setting off and then on.
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
@@ -3825,14 +3681,13 @@
@Test
public void testMeteredMultipathPreferenceSetting() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
- final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker();
final String settingName = Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
for (int config : Arrays.asList(0, 3, 2)) {
for (String setting: Arrays.asList(null, "0", "2", "1")) {
- tracker.configMeteredMultipathPreference = config;
+ mPolicyTracker.mConfigMeteredMultipathPreference = config;
Settings.Global.putString(cr, settingName, setting);
- tracker.reevaluate();
+ mPolicyTracker.reevaluate();
waitForIdle();
final int expected = (setting != null) ? Integer.parseInt(setting) : config;
@@ -3847,13 +3702,13 @@
* time-out period expires.
*/
@Test
- public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() {
+ public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
@@ -3867,18 +3722,18 @@
* not trigger onUnavailable() once the time-out period expires.
*/
@Test
- public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() {
+ public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -3890,7 +3745,7 @@
* (somehow) satisfied - the callback isn't called later.
*/
@Test
- public void testTimedoutNetworkRequest() {
+ public void testTimedoutNetworkRequest() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3898,10 +3753,10 @@
mCm.requestNetwork(nr, networkCallback, timeoutMs);
// pass timeout and validate that UNAVAILABLE is called
- networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
// create a network satisfying request - validate that request not triggered
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.assertNoCallback();
}
@@ -3911,7 +3766,7 @@
* trigger the callback.
*/
@Test
- public void testNoCallbackAfterUnregisteredNetworkRequest() {
+ public void testNoCallbackAfterUnregisteredNetworkRequest() throws Exception {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -3924,7 +3779,7 @@
networkCallback.assertNoCallback();
// create a network satisfying request - validate that request not triggered
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
networkCallback.assertNoCallback();
}
@@ -3989,7 +3844,7 @@
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
testFactory.triggerUnfulfillable(requests.get(newRequestId));
- networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
@@ -3997,13 +3852,13 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- testFactory.unregister();
+ testFactory.terminate();
handlerThread.quit();
}
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
- public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+ public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }
private class CallbackValue {
public CallbackType callbackType;
@@ -4051,25 +3906,19 @@
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
}
- private void expectCallback(CallbackValue callbackValue) {
- try {
- assertEquals(
- callbackValue,
- mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
- }
+ private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+ assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
- public void expectStarted() {
+ public void expectStarted() throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
}
- public void expectStopped() {
+ public void expectStopped() throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
}
- public void expectError(int error) {
+ public void expectError(int error) throws Exception {
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
}
}
@@ -4130,25 +3979,20 @@
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error));
}
- private void expectCallback(CallbackValue callbackValue) {
- try {
- assertEquals(
- callbackValue,
- mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } catch (InterruptedException e) {
- fail(callbackValue.callbackType + " callback not seen after " + TIMEOUT_MS + " ms");
- }
+ private void expectCallback(CallbackValue callbackValue) throws InterruptedException {
+ assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
}
- public void expectStarted() {
+ public void expectStarted() throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_STARTED));
}
- public void expectStopped() {
+ public void expectStopped() throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_STOPPED));
}
- public void expectError(int error) {
+ public void expectError(int error) throws InterruptedException {
expectCallback(new CallbackValue(CallbackType.ON_ERROR, error));
}
@@ -4159,14 +4003,14 @@
}
}
- private Network connectKeepaliveNetwork(LinkProperties lp) {
+ private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception {
// Ensure the network is disconnected before we do anything.
if (mWiFiNetworkAgent != null) {
assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
}
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -4176,6 +4020,7 @@
}
@Test
+ @FlakyTest(bugId = 140305589)
public void testPacketKeepalives() throws Exception {
InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
@@ -4229,10 +4074,10 @@
callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
// Check that a started keepalive can be stopped.
- mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
- mWiFiNetworkAgent.setStopKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS);
ka.stop();
callback.expectStopped();
@@ -4250,7 +4095,7 @@
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4);
callback.expectStarted();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK);
// ... and that stopping it after that has no adverse effects.
@@ -4261,7 +4106,7 @@
// Reconnect.
myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(PacketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS);
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4292,13 +4137,9 @@
callback3.expectStopped();
}
- @FunctionalInterface
- private interface ThrowingConsumer<T> {
- void accept(T t) throws Exception;
- }
-
// Helper method to prepare the executor and run test
- private void runTestWithSerialExecutors(ThrowingConsumer<Executor> functor) throws Exception {
+ private void runTestWithSerialExecutors(ExceptionUtils.ThrowingConsumer<Executor> functor)
+ throws Exception {
final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
final Executor executorInline = (Runnable r) -> r.run();
functor.accept(executorSingleThread);
@@ -4385,12 +4226,12 @@
}
// Check that a started keepalive can be stopped.
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
try (SocketKeepalive ka = mCm.createSocketKeepalive(
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
ka.start(validKaInterval);
callback.expectStarted();
- mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
ka.stop();
callback.expectStopped();
@@ -4430,7 +4271,7 @@
ka.start(validKaInterval);
callback.expectStarted();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK);
// ... and that stopping it after that has no adverse effects.
@@ -4443,7 +4284,7 @@
// Reconnect.
myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
@@ -4480,7 +4321,7 @@
// assertFalse(isUdpPortInUse(srcPort2));
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
@@ -4556,7 +4397,7 @@
testSocketV6.close();
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
@@ -4572,8 +4413,8 @@
lp.addLinkAddress(new LinkAddress(myIPv4, 25));
lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
Network myNet = connectKeepaliveNetwork(lp);
- mWiFiNetworkAgent.setStartKeepaliveError(SocketKeepalive.SUCCESS);
- mWiFiNetworkAgent.setStopKeepaliveError(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+ mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS);
TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor);
@@ -4609,14 +4450,14 @@
// assertFalse(isUdpPortInUse(srcPort));
mWiFiNetworkAgent.disconnect();
- waitFor(mWiFiNetworkAgent.getDisconnectedCV());
+ mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent = null;
}
private static boolean isUdpPortInUse(int port) {
try (DatagramSocket ignored = new DatagramSocket(port)) {
return false;
- } catch (IOException ignored) {
+ } catch (IOException alreadyInUse) {
return true;
}
}
@@ -4628,23 +4469,19 @@
}
private static class TestNetworkPinner extends NetworkPinner {
- public static boolean awaitPin(int timeoutMs) {
+ public static boolean awaitPin(int timeoutMs) throws InterruptedException {
synchronized(sLock) {
if (sNetwork == null) {
- try {
- sLock.wait(timeoutMs);
- } catch (InterruptedException e) {}
+ sLock.wait(timeoutMs);
}
return sNetwork != null;
}
}
- public static boolean awaitUnpin(int timeoutMs) {
+ public static boolean awaitUnpin(int timeoutMs) throws InterruptedException {
synchronized(sLock) {
if (sNetwork != null) {
- try {
- sLock.wait(timeoutMs);
- } catch (InterruptedException e) {}
+ sLock.wait(timeoutMs);
}
return sNetwork == null;
}
@@ -4667,7 +4504,7 @@
}
@Test
- public void testNetworkPinner() {
+ public void testNetworkPinner() throws Exception {
NetworkRequest wifiRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI)
.build();
@@ -4676,9 +4513,9 @@
TestNetworkPinner.pin(mServiceContext, wifiRequest);
assertNull(mCm.getBoundNetworkForProcess());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
// When wi-fi connects, expect to be pinned.
@@ -4691,7 +4528,7 @@
assertNotPinnedToWifi();
// Reconnecting does not cause the pin to come back.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
assertFalse(TestNetworkPinner.awaitPin(100));
assertNotPinnedToWifi();
@@ -4706,21 +4543,21 @@
assertNotPinnedToWifi();
// Disconnect cell and wifi.
- ConditionVariable cv = waitForConnectivityBroadcasts(3); // cell down, wifi up, wifi down.
+ ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down.
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
waitFor(cv);
// Pinning takes effect even if the pinned network is the default when the pin is set...
TestNetworkPinner.pin(mServiceContext, wifiRequest);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
assertTrue(TestNetworkPinner.awaitPin(100));
assertPinnedToWifiWithWifiDefault();
// ... and is maintained even when that network is no longer the default.
- cv = waitForConnectivityBroadcasts(1);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ cv = registerConnectivityBroadcast(1);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
waitFor(cv);
assertPinnedToWifiWithCellDefault();
@@ -4762,25 +4599,20 @@
}
// Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
- try {
- mCm.requestNetwork(networkRequest, new NetworkCallback());
- fail("Registering " + MAX_REQUESTS + " network requests did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.registerNetworkCallback(networkRequest, new NetworkCallback());
- fail("Registering " + MAX_REQUESTS + " network callbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.requestNetwork(networkRequest,
- PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0));
- fail("Registering " + MAX_REQUESTS + " PendingIntent requests did not throw exception");
- } catch (TooManyRequestsException expected) {}
- try {
- mCm.registerNetworkCallback(networkRequest,
- PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0));
- fail("Registering " + MAX_REQUESTS
- + " PendingIntent callbacks did not throw exception");
- } catch (TooManyRequestsException expected) {}
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.requestNetwork(networkRequest, new NetworkCallback())
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.registerNetworkCallback(networkRequest, new NetworkCallback())
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.requestNetwork(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("c"), 0))
+ );
+ assertThrows(TooManyRequestsException.class, () ->
+ mCm.registerNetworkCallback(networkRequest,
+ PendingIntent.getBroadcast(mContext, 0, new Intent("d"), 0))
+ );
for (Object o : registered) {
if (o instanceof NetworkCallback) {
@@ -4824,11 +4656,11 @@
}
@Test
- public void testNetworkInfoOfTypeNone() {
- ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
+ public void testNetworkInfoOfTypeNone() throws Exception {
+ ConditionVariable broadcastCV = registerConnectivityBroadcast(1);
verifyNoNetwork();
- MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+ TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
assertNull(mCm.getActiveNetworkInfo());
Network[] allNetworks = mCm.getAllNetworks();
@@ -4843,7 +4675,7 @@
mCm.registerNetworkCallback(request, callback);
// Bring up wifi aware network.
- wifiAware.connect(false, false);
+ wifiAware.connect(false, false, false /* isStrictMode */);
callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
@@ -4854,7 +4686,7 @@
// Disconnect wifi aware network.
wifiAware.disconnect();
- callback.expectCallbackLike((info) -> info.state == CallbackState.LOST, TIMEOUT_MS);
+ callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackEntry.Lost);
mCm.unregisterNetworkCallback(callback);
verifyNoNetwork();
@@ -4871,21 +4703,21 @@
assertNull(mCm.getLinkProperties(TYPE_NONE));
assertFalse(mCm.isNetworkSupported(TYPE_NONE));
- assertException(() -> { mCm.networkCapabilitiesForType(TYPE_NONE); },
- IllegalArgumentException.class);
+ assertThrows(IllegalArgumentException.class,
+ () -> mCm.networkCapabilitiesForType(TYPE_NONE));
Class<UnsupportedOperationException> unsupported = UnsupportedOperationException.class;
- assertException(() -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
- assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
+ assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_WIFI, ""));
+ assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_WIFI, ""));
// TODO: let test context have configuration application target sdk version
// and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED
- assertException(() -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
- assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
- assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported);
+ assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_NONE, ""));
+ assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_NONE, ""));
+ assertThrows(unsupported, () -> mCm.requestRouteToHostAddress(TYPE_NONE, null));
}
@Test
- public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() {
+ public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() throws Exception {
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -4901,16 +4733,17 @@
// Verify direct routes are added when network agent is first registered in
// ConnectivityService.
- MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp);
+ TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
networkAgent.connect(true);
- networkCallback.expectCallback(CallbackState.AVAILABLE, networkAgent);
- networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
- CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ networkCallback.expectCallback(CallbackEntry.AVAILABLE, networkAgent);
+ networkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi =
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
networkAgent);
- networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
+ networkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, networkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
networkCallback.assertNoCallback();
- checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
+ checkDirectlyConnectedRoutes(cbi.getLp(), Arrays.asList(myIpv4Address),
Arrays.asList(myIpv4DefaultRoute));
checkDirectlyConnectedRoutes(mCm.getLinkProperties(networkAgent.getNetwork()),
Arrays.asList(myIpv4Address), Arrays.asList(myIpv4DefaultRoute));
@@ -4922,9 +4755,9 @@
newLp.addLinkAddress(myIpv6Address1);
newLp.addLinkAddress(myIpv6Address2);
networkAgent.sendLinkProperties(newLp);
- cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, networkAgent);
+ cbi = networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent);
networkCallback.assertNoCallback();
- checkDirectlyConnectedRoutes(cbi.arg,
+ checkDirectlyConnectedRoutes(cbi.getLp(),
Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2),
Arrays.asList(myIpv4DefaultRoute));
mCm.unregisterNetworkCallback(networkCallback);
@@ -4932,8 +4765,8 @@
@Test
public void testStatsIfacesChanged() throws Exception {
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
@@ -4948,11 +4781,8 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4961,65 +4791,47 @@
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyWifi),
- any(NetworkState[].class),
- eq(WIFI_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
+ eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Captive portal change shouldn't update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
waitForIdle();
verify(mStatsService, never())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(
- eq(onlyCell),
- any(NetworkState[].class),
- eq(MOBILE_IFNAME));
- assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
+ .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
+ eq(new VpnInfo[0]));
reset(mStatsService);
}
@@ -5030,7 +4842,7 @@
// Clear any interactions that occur as a result of CS starting up.
reset(mMockDnsResolver);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
verify(mMockDnsResolver, never()).setResolverConfiguration(any());
verifyNoMoreInteractions(mMockDnsResolver);
@@ -5101,6 +4913,67 @@
}
@Test
+ public void testDnsConfigurationTransTypesPushed() throws Exception {
+ // Clear any interactions that occur as a result of CS starting up.
+ reset(mMockDnsResolver);
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ verify(mMockDnsResolver, times(1)).createNetworkCache(
+ eq(mWiFiNetworkAgent.getNetwork().netId));
+ verify(mMockDnsResolver, times(2)).setResolverConfiguration(
+ mResolverParamsParcelCaptor.capture());
+ final ResolverParamsParcel resolverParams = mResolverParamsParcelCaptor.getValue();
+ assertContainsExactly(resolverParams.transportTypes, TRANSPORT_WIFI);
+ reset(mMockDnsResolver);
+ }
+
+ @Test
+ public void testPrivateDnsNotification() throws Exception {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+ // Bring up wifi.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // Private DNS resolution failed, checking if the notification will be shown or not.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ // If network validation failed, NetworkMonitor will re-evaluate the network.
+ // ConnectivityService should filter the redundant notification. This part is trying to
+ // simulate that situation and check if ConnectivityService could filter that case.
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be
+ // shown.
+ mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL));
+ // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be
+ // shown again.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ }
+
+ @Test
public void testPrivateDnsSettingsChange() throws Exception {
// Clear any interactions that occur as a result of CS starting up.
reset(mMockDnsResolver);
@@ -5113,7 +4986,7 @@
.addTransportType(TRANSPORT_CELLULAR).build();
mCm.requestNetwork(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
// CS tells netd about the empty DNS config for this network.
verify(mMockDnsResolver, never()).setResolverConfiguration(any());
@@ -5143,21 +5016,21 @@
ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue();
assertEquals(2, resolvrParams.tlsServers.length);
assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers,
- new String[]{"2001:db8::1", "192.0.2.1"}));
+ new String[] { "2001:db8::1", "192.0.2.1" }));
// Opportunistic mode.
assertEquals(2, resolvrParams.tlsServers.length);
assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers,
- new String[]{"2001:db8::1", "192.0.2.1"}));
+ new String[] { "2001:db8::1", "192.0.2.1" }));
reset(mMockDnsResolver);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
mCellNetworkAgent);
- CallbackInfo cbi = cellNetworkCallback.expectCallback(
- CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ assertFalse(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
verify(mMockDnsResolver, times(1)).setResolverConfiguration(
@@ -5165,7 +5038,7 @@
resolvrParams = mResolverParamsParcelCaptor.getValue();
assertEquals(2, resolvrParams.servers.length);
assertTrue(ArrayUtils.containsAll(resolvrParams.servers,
- new String[]{"2001:db8::1", "192.0.2.1"}));
+ new String[] { "2001:db8::1", "192.0.2.1" }));
reset(mMockDnsResolver);
cellNetworkCallback.assertNoCallback();
@@ -5175,21 +5048,21 @@
resolvrParams = mResolverParamsParcelCaptor.getValue();
assertEquals(2, resolvrParams.servers.length);
assertTrue(ArrayUtils.containsAll(resolvrParams.servers,
- new String[]{"2001:db8::1", "192.0.2.1"}));
+ new String[] { "2001:db8::1", "192.0.2.1" }));
assertEquals(2, resolvrParams.tlsServers.length);
assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers,
- new String[]{"2001:db8::1", "192.0.2.1"}));
+ new String[] { "2001:db8::1", "192.0.2.1" }));
reset(mMockDnsResolver);
cellNetworkCallback.assertNoCallback();
setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
// Can't test dns configuration for strict mode without properly mocking
// out the DNS lookups, but can test that LinkProperties is updated.
- cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ assertTrue(cbi.getLp().isPrivateDnsActive());
+ assertEquals("strict.example.com", cbi.getLp().getPrivateDnsServerName());
}
@Test
@@ -5202,23 +5075,23 @@
.addTransportType(TRANSPORT_CELLULAR).build();
mCm.requestNetwork(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
waitForIdle();
LinkProperties lp = new LinkProperties();
mCellNetworkAgent.sendLinkProperties(lp);
mCellNetworkAgent.connect(false);
waitForIdle();
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
mCellNetworkAgent);
- CallbackInfo cbi = cellNetworkCallback.expectCallback(
- CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ assertFalse(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
Set<InetAddress> dnsServers = new HashSet<>();
- checkDnsServers(cbi.arg, dnsServers);
+ checkDnsServers(cbi.getLp(), dnsServers);
// Send a validation event for a server that is not part of the current
// resolver config. The validation event should be ignored.
@@ -5230,13 +5103,13 @@
LinkProperties lp2 = new LinkProperties(lp);
lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
mCellNetworkAgent.sendLinkProperties(lp2);
- cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ assertFalse(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
dnsServers.add(InetAddress.getByName("145.100.185.16"));
- checkDnsServers(cbi.arg, dnsServers);
+ checkDnsServers(cbi.getLp(), dnsServers);
// Send a validation event containing a hostname that is not part of
// the current resolver config. The validation event should be ignored.
@@ -5254,39 +5127,39 @@
// private dns fields should be sent.
mService.mNetdEventCallback.onPrivateDnsValidationEvent(
mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
- cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
- checkDnsServers(cbi.arg, dnsServers);
+ assertTrue(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
+ checkDnsServers(cbi.getLp(), dnsServers);
// The private dns fields in LinkProperties should be preserved when
// the network agent sends unrelated changes.
LinkProperties lp3 = new LinkProperties(lp2);
lp3.setMtu(1300);
mCellNetworkAgent.sendLinkProperties(lp3);
- cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
- checkDnsServers(cbi.arg, dnsServers);
- assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+ assertTrue(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
+ checkDnsServers(cbi.getLp(), dnsServers);
+ assertEquals(1300, cbi.getLp().getMtu());
// Removing the only validated server should affect the private dns
// fields in LinkProperties.
LinkProperties lp4 = new LinkProperties(lp3);
lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
mCellNetworkAgent.sendLinkProperties(lp4);
- cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
- assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
- assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ assertFalse(cbi.getLp().isPrivateDnsActive());
+ assertNull(cbi.getLp().getPrivateDnsServerName());
dnsServers.remove(InetAddress.getByName("145.100.185.16"));
- checkDnsServers(cbi.arg, dnsServers);
- assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+ checkDnsServers(cbi.getLp(), dnsServers);
+ assertEquals(1300, cbi.getLp().getMtu());
}
private void checkDirectlyConnectedRoutes(Object callbackObj,
@@ -5313,31 +5186,8 @@
assertTrue(lp.getDnsServers().containsAll(dnsServers));
}
- private static <T> void assertEmpty(T[] ts) {
- int length = ts.length;
- assertEquals("expected empty array, but length was " + length, 0, length);
- }
-
- private static <T> void assertLength(int expected, T[] got) {
- int length = got.length;
- assertEquals(String.format("expected array of length %s, but length was %s for %s",
- expected, length, Arrays.toString(got)), expected, length);
- }
-
- private static <T> void assertException(Runnable block, Class<T> expected) {
- try {
- block.run();
- fail("Expected exception of type " + expected);
- } catch (Exception got) {
- if (!got.getClass().equals(expected)) {
- fail("Expected exception of type " + expected + " but got " + got);
- }
- return;
- }
- }
-
@Test
- public void testVpnNetworkActive() {
+ public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
@@ -5360,7 +5210,7 @@
mCm.registerDefaultNetworkCallback(defaultCallback);
defaultCallback.assertNoCallback();
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -5370,15 +5220,17 @@
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
- assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
- vpnNetworkAgent.setNetworkValid();
+ assertFalse(NetworkMonitorUtils.isValidationRequired(
+ vpnNetworkAgent.getNetworkCapabilities()));
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.connect(false);
mMockVpn.connect();
@@ -5391,19 +5243,19 @@
defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
vpnNetworkAgent.setUids(ranges);
- genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
// TODO : The default network callback should actually get a LOST call here (also see the
// comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -5411,7 +5263,7 @@
// can't currently update their UIDs without disconnecting, so this does not matter too
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
- defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
@@ -5423,23 +5275,23 @@
vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
- defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- genericNotVpnNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ genericNotVpnNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -5449,24 +5301,26 @@
}
@Test
- public void testVpnWithoutInternet() {
+ public void testVpnWithoutInternet() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.assertNoCallback();
@@ -5479,31 +5333,33 @@
}
@Test
- public void testVpnWithInternet() {
+ public void testVpnWithInternet() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5515,25 +5371,25 @@
mCm.registerDefaultNetworkCallback(callback);
// Bring up Ethernet.
- mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- // TODO: this looks like a spurious callback.
- callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
callback.assertNoCallback();
assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
@@ -5544,12 +5400,13 @@
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
- assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+ assertFalse(NetworkMonitorUtils.isValidationRequired(
+ vpnNetworkAgent.getNetworkCapabilities()));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
- vpnNetworkAgent.mNetworkCapabilities));
+ vpnNetworkAgent.getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
@@ -5557,12 +5414,53 @@
callback.assertNoCallback();
vpnNetworkAgent.disconnect();
- callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@Test
- public void testVpnSetUnderlyingNetworks() {
+ public void testVpnStartsWithUnderlyingCaps() throws Exception {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+ vpnNetworkCallback.assertNoCallback();
+
+ // Connect cell. It will become the default network, and in the absence of setting
+ // underlying networks explicitly it will become the sole underlying network for the vpn.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+ mMockVpn.setUids(ranges);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
+
+ vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+ false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+ nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
+
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ }
+
+ @Test
+ public void testVpnSetUnderlyingNetworks() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
@@ -5574,13 +5472,15 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5589,78 +5489,134 @@
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
// For safety reasons a VPN without underlying networks is considered metered.
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ // A VPN without underlying networks is not suspended.
+ assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// Connect cell and use it as an underlying network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mWiFiNetworkAgent.connect(true);
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// Don't disconnect, but note the VPN is not using wifi any more.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // Use Wifi but not cell. Note the VPN is now unmetered.
+ // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+
+ // Add NOT_SUSPENDED again and observe VPN is no longer suspended.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+
+ // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
- && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// Use both again.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+
+ // Cell is suspended again. As WiFi is not, this should not cause a callback.
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ vpnNetworkCallback.assertNoCallback();
+
+ // Stop using WiFi. The VPN is suspended again.
+ mService.setUnderlyingNetworksForVpn(
+ new Network[] { mCellNetworkAgent.getNetwork() });
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ // While the SUSPENDED callback should in theory be sent here, it is not. This is
+ // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
+ // been public and are deprecated and slated for removal, there is no sense in spending
+ // resources fixing this bug now.
+
+ // Use both again.
+ mService.setUnderlyingNetworksForVpn(
+ new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
+
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
+ // worth anybody's time to fix.
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
mCellNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
- && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
mMockVpn.disconnect();
}
@Test
- public void testNullUnderlyingNetworks() {
+ public void testNullUnderlyingNetworks() throws Exception {
final int uid = Process.myUid();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
@@ -5672,13 +5628,15 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5690,23 +5648,23 @@
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect to Cell; Cell is the default network.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect to WiFi; WiFi is the new default.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
- && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect Cell. The default network did not change, so there shouldn't be any changes in
// the capabilities.
@@ -5715,19 +5673,19 @@
// Disconnect wifi too. Now we have no default network.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ (caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
- && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
- vpnNetworkAgent);
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
mMockVpn.disconnect();
}
@Test
- public void testIsActiveNetworkMeteredOverWifi() {
+ public void testIsActiveNetworkMeteredOverWifi() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
@@ -5736,10 +5694,10 @@
}
@Test
- public void testIsActiveNetworkMeteredOverCell() {
+ public void testIsActiveNetworkMeteredOverCell() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
@@ -5748,17 +5706,18 @@
}
@Test
- public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() {
+ public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
assertTrue(mCm.isActiveNetworkMetered());
// Connect VPN network. By default it is using current default network (Cell).
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5774,7 +5733,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// Connect WiFi.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
@@ -5802,23 +5761,24 @@
}
@Test
- public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() {
+ public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
waitForIdle();
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5873,17 +5833,18 @@
}
@Test
- public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() {
+ public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
final int uid = Process.myUid();
ranges.add(new UidRange(uid, uid));
@@ -5920,28 +5881,28 @@
}
@Test
- public void testNetworkBlockedStatus() {
+ public void testNetworkBlockedStatus() throws Exception {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_REJECT_ALL);
+ setUidRulesChanged(RULE_REJECT_ALL);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
// ConnectivityService should cache it not to invoke the callback again.
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.assertNoCallback();
- mService.setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
// Restrict the network based on UID rule and NOT_METERED capability change.
@@ -5952,18 +5913,18 @@
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_ALLOW_METERED);
+ setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
- mService.setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_NONE);
cellNetworkCallback.assertNoCallback();
// Restrict the network based on BackgroundRestricted.
- mService.setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(true);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
- mService.setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(true);
cellNetworkCallback.assertNoCallback();
- mService.setRestrictBackgroundChanged(false);
+ setRestrictBackgroundChanged(false);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
@@ -5971,30 +5932,30 @@
}
@Test
- public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+ public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
// No Networkcallbacks invoked before any network is active.
- mService.setUidRulesChanged(RULE_REJECT_ALL);
- mService.setUidRulesChanged(RULE_NONE);
- mService.setUidRulesChanged(RULE_REJECT_METERED);
+ setUidRulesChanged(RULE_REJECT_ALL);
+ setUidRulesChanged(RULE_NONE);
+ setUidRulesChanged(RULE_REJECT_METERED);
defaultCallback.assertNoCallback();
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
// Allow to use the network after switching to NOT_METERED network.
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
// Switch to METERED network. Restrict the use of the network.
mWiFiNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
// Network becomes NOT_METERED.
@@ -6003,17 +5964,80 @@
defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
// Verify there's no Networkcallbacks invoked after data saver on/off.
- mService.setRestrictBackgroundChanged(true);
- mService.setRestrictBackgroundChanged(false);
+ setRestrictBackgroundChanged(true);
+ setRestrictBackgroundChanged(false);
defaultCallback.assertNoCallback();
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
}
+ @Test
+ public final void testLoseTrusted() throws Exception {
+ final NetworkRequest trustedRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .build();
+ final TestNetworkCallback trustedCallback = new TestNetworkCallback();
+ mCm.requestNetwork(trustedRequest, trustedCallback);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mNetworkManagementService);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mNetworkManagementService);
+
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+ trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mNetworkManagementService);
+
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+ trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ verify(mNetworkManagementService).clearDefaultNetId();
+
+ mCm.unregisterNetworkCallback(trustedCallback);
+ }
+
+ @Ignore // 40%+ flakiness : figure out why and re-enable.
+ @Test
+ public final void testBatteryStatsNetworkType() throws Exception {
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("cell0");
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
+ reset(mBatteryStatsService);
+
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wifi0");
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ mWiFiNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
+ TYPE_WIFI);
+ reset(mBatteryStatsService);
+
+ mCellNetworkAgent.disconnect();
+
+ cellLp.setInterfaceName("wifi0");
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
+ }
+
/**
* Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
*/
@@ -6040,11 +6064,20 @@
}
@Test
- public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+ public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
final String kNat64PrefixString = "2001:db8:64:64:64:64::";
final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96);
+ final String kOtherNat64PrefixString = "64:ff9b::";
+ final IpPrefix kOtherNat64Prefix = new IpPrefix(
+ InetAddress.getByName(kOtherNat64PrefixString), 96);
+ final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
+ MOBILE_IFNAME);
+ final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
+ final RouteInfo ipv4Subnet = new RouteInfo(myIpv4, null, MOBILE_IFNAME);
+ final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(),
+ CLAT_PREFIX + MOBILE_IFNAME);
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
@@ -6054,25 +6087,27 @@
mCm.registerNetworkCallback(networkRequest, networkCallback);
// Prepare ipv6 only link properties.
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
- final int cellNetId = mCellNetworkAgent.getNetwork().netId;
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
cellLp.addLinkAddress(myIpv6);
- cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
- cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+ cellLp.addRoute(defaultRoute);
+ cellLp.addRoute(ipv6Subnet);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mNetworkManagementService);
reset(mMockDnsResolver);
reset(mMockNetd);
- when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfig(myIpv4));
+ reset(mBatteryStatsService);
// Connect with ipv6 link properties. Expect prefix discovery to be started.
- mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(true);
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ waitForIdle();
verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
+ assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -6084,44 +6119,54 @@
// the NAT64 prefix was removed because one was never discovered.
cellLp.addLinkAddress(myIpv4);
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ assertRoutesAdded(cellNetId, ipv4Subnet);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
+ // Make sure BatteryStats was not told about any v4- interfaces, as none should have
+ // come online yet.
+ waitForIdle();
+ verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
+ reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
// Remove IPv4 address. Expect prefix discovery to be started again.
cellLp.removeLinkAddress(myIpv4);
- cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
+ assertRoutesRemoved(cellNetId, ipv4Subnet);
// When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
- Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+ Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent);
assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
- LinkProperties lpBeforeClat = (LinkProperties) networkCallback.expectCallback(
- CallbackState.LINK_PROPERTIES, mCellNetworkAgent).arg;
+ LinkProperties lpBeforeClat = networkCallback.expectCallback(
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
assertEquals(0, lpBeforeClat.getStackedLinks().size());
assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
.getStackedLinks();
assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+ assertRoutesAdded(cellNetId, stackedDefault);
// Change trivial linkproperties and see if stacked link is preserved.
cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
List<LinkProperties> stackedLpsAfterChange =
mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
@@ -6134,22 +6179,48 @@
assertEquals(1, resolvrParams.servers.length);
assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
+ for (final LinkProperties stackedLp : stackedLpsAfterChange) {
+ verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
+ TYPE_MOBILE);
+ }
+ reset(mMockNetd);
+
+ // Change the NAT64 prefix without first removing it.
+ // Expect clatd to be stopped and started with the new prefix.
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+ kOtherNat64PrefixString, 96);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 0);
+ verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ assertRoutesRemoved(cellNetId, stackedDefault);
+
+ verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
+ clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 1);
+ assertRoutesAdded(cellNetId, stackedDefault);
+ reset(mMockNetd);
+
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
- cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ cellLp.addRoute(ipv4Subnet);
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ assertRoutesAdded(cellNetId, ipv4Subnet);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
// As soon as stop is called, the linkproperties lose the stacked interface.
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
LinkProperties expected = new LinkProperties(cellLp);
- expected.setNat64Prefix(kNat64Prefix);
+ expected.setNat64Prefix(kOtherNat64Prefix);
assertEquals(expected, actualLpAfterIpv4);
assertEquals(0, actualLpAfterIpv4.getStackedLinks().size());
+ assertRoutesRemoved(cellNetId, stackedDefault);
// The interface removed callback happens but has no effect after stop is called.
clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
@@ -6157,60 +6228,238 @@
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
+ reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
- kNat64PrefixString, 96);
- networkCallback.expectLinkPropertiesLike((lp) -> lp.getNat64Prefix() == null,
- mCellNetworkAgent);
+ kOtherNat64PrefixString, 96);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getNat64Prefix() == null);
// Remove IPv4 address and expect prefix discovery and clatd to be started again.
cellLp.removeLinkAddress(myIpv4);
cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ assertRoutesRemoved(cellNetId, ipv4Subnet); // Directly-connected routes auto-added.
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
- networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
-
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
- networkCallback.expectLinkPropertiesLike(
- (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null,
- mCellNetworkAgent);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null);
+ assertRoutesAdded(cellNetId, stackedDefault);
// NAT64 prefix is removed. Expect that clat is stopped.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
kNat64PrefixString, 96);
- networkCallback.expectLinkPropertiesLike(
- (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null,
- mCellNetworkAgent);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null);
+ assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault);
+
+ // Stop has no effect because clat is already stopped.
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
- networkCallback.expectLinkPropertiesLike((lp) -> lp.getStackedLinks().size() == 0,
- mCellNetworkAgent);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ (lp) -> lp.getStackedLinks().size() == 0);
+ verifyNoMoreInteractions(mMockNetd);
// Clean up.
mCellNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(networkCallback);
}
+ private void expectNat64PrefixChange(TestableNetworkCallback callback,
+ TestNetworkAgentWrapper agent, IpPrefix prefix) {
+ callback.expectLinkPropertiesThat(agent, x -> Objects.equals(x.getNat64Prefix(), prefix));
+ }
+
@Test
- public void testDataActivityTracking() throws RemoteException {
+ public void testNat64PrefixMultipleSources() throws Exception {
+ final String iface = "wlan0";
+ final String pref64FromRaStr = "64:ff9b::";
+ final String pref64FromDnsStr = "2001:db8:64::";
+ final IpPrefix pref64FromRa = new IpPrefix(InetAddress.getByName(pref64FromRaStr), 96);
+ final IpPrefix pref64FromDns = new IpPrefix(InetAddress.getByName(pref64FromDnsStr), 96);
+ final IpPrefix newPref64FromRa = new IpPrefix("2001:db8:64:64:64:64::/96");
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ final LinkProperties baseLp = new LinkProperties();
+ baseLp.setInterfaceName(iface);
+ baseLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+ baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464"));
+
+ reset(mMockNetd, mMockDnsResolver);
+ InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
+
+ // If a network already has a NAT64 prefix on connect, clatd is started immediately and
+ // prefix discovery is never started.
+ LinkProperties lp = new LinkProperties(baseLp);
+ lp.setNat64Prefix(pref64FromRa);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mCellNetworkAgent.connect(false);
+ final Network network = mCellNetworkAgent.getNetwork();
+ int netId = network.getNetId();
+ callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ callback.assertNoCallback();
+ assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
+
+ // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+ inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+
+ // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
+ // clatd is started with the prefix from the RA.
+ lp.setNat64Prefix(pref64FromRa);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+
+ // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
+ // discovery has succeeded.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+ inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+
+ mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+ pref64FromDnsStr, 96);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+
+ // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
+ // discovery is not stopped, and there are no callbacks.
+ lp.setNat64Prefix(pref64FromDns);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // If the RA is later withdrawn, nothing happens again.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
+ lp.setNat64Prefix(pref64FromRa);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+
+ // Stopping prefix discovery results in a prefix removed notification.
+ mService.mNetdEventCallback.onNat64PrefixEvent(netId, false /* added */,
+ pref64FromDnsStr, 96);
+
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+
+ // If the RA prefix changes, clatd is restarted and prefix discovery is not started.
+ lp.setNat64Prefix(newPref64FromRa);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+ inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+
+ // If the RA prefix changes to the same value, nothing happens.
+ lp.setNat64Prefix(newPref64FromRa);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // The transition between no prefix and DNS prefix is tested in testStackedLinkProperties.
+
+ // If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
+ // (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
+ lp.setNat64Prefix(null);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+ inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+ mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+ pref64FromDnsStr, 96);
+ expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
+
+ lp.setNat64Prefix(pref64FromDns);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ callback.assertNoCallback();
+ inOrder.verify(mMockNetd, never()).clatdStop(iface);
+ inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
+ // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
+ // clat has been stopped, or the test will be flaky.
+ ConditionVariable cv = registerConnectivityBroadcast(1);
+ mCellNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ waitFor(cv);
+
+ inOrder.verify(mMockNetd).clatdStop(iface);
+ inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+ inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
+ public void testDataActivityTracking() throws Exception {
final TestNetworkCallback networkCallback = new TestNetworkCallback();
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_INTERNET)
.build();
mCm.registerNetworkCallback(networkRequest, networkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellNetworkAgent.sendLinkProperties(cellLp);
@@ -6220,7 +6469,7 @@
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
eq(ConnectivityManager.TYPE_MOBILE));
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
@@ -6229,7 +6478,7 @@
reset(mNetworkManagementService);
mWiFiNetworkAgent.connect(true);
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
eq(ConnectivityManager.TYPE_WIFI));
@@ -6238,26 +6487,26 @@
// Disconnect wifi and switch back to cell
reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertNoCallbacks(networkCallback);
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
eq(ConnectivityManager.TYPE_MOBILE));
// reconnect wifi
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
mWiFiNetworkAgent.connect(true);
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
// Disconnect cell
reset(mNetworkManagementService);
reset(mMockNetd);
mCellNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
// LOST callback is triggered earlier than removing idle timer. Broadcast should also be
// sent as network being switched. Ensure rule removal for cell will not be triggered
// unexpectedly before network being removed.
@@ -6268,7 +6517,7 @@
.destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId));
// Disconnect wifi
- ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ ConditionVariable cv = registerConnectivityBroadcast(1);
reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
waitFor(cv);
@@ -6278,49 +6527,58 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private void verifyTcpBufferSizeChange(String tcpBufferSizes) {
+ private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
String[] values = tcpBufferSizes.split(",");
String rmemValues = String.join(" ", values[0], values[1], values[2]);
String wmemValues = String.join(" ", values[3], values[4], values[5]);
- waitForIdle();
- try {
- verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
- } catch (RemoteException e) {
- fail("mMockNetd should never throw RemoteException");
- }
+ verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
reset(mMockNetd);
}
@Test
- public void testTcpBufferReset() {
+ public void testTcpBufferReset() throws Exception {
final String testTcpBufferSizes = "1,2,3,4,5,6";
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
- mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
reset(mMockNetd);
// Switching default network updates TCP buffer sizes.
mCellNetworkAgent.connect(false);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
// Change link Properties should have updated tcp buffer size.
LinkProperties lp = new LinkProperties();
lp.setTcpBufferSizes(testTcpBufferSizes);
mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verifyTcpBufferSizeChange(testTcpBufferSizes);
+
+ // Clean up.
+ mCellNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ networkCallback.assertNoCallback();
+ mCm.unregisterNetworkCallback(networkCallback);
}
@Test
- public void testGetGlobalProxyForNetwork() {
+ public void testGetGlobalProxyForNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
}
@Test
- public void testGetProxyForActiveNetwork() {
+ public void testGetProxyForActiveNetwork() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
@@ -6335,18 +6593,19 @@
}
@Test
- public void testGetProxyForVPN() {
+ public void testGetProxyForVPN() throws Exception {
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
// Set up a WiFi network with no proxy
- mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
// Set up a VPN network with a proxy
final int uid = Process.myUid();
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
@@ -6393,9 +6652,10 @@
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6418,10 +6678,12 @@
public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+ lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6436,7 +6698,8 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, Process.SYSTEM_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
+ lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6447,9 +6710,10 @@
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6483,6 +6747,7 @@
reset(mMockNetd);
lp = new LinkProperties();
lp.setInterfaceName("tun1");
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
vpnNetworkAgent.sendLinkProperties(lp);
waitForIdle();
@@ -6495,10 +6760,11 @@
public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- final MockNetworkAgent vpnNetworkAgent = establishVpn(lp, VPN_UID,
+ final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
Collections.singleton(vpnRange));
reset(mMockNetd);
@@ -6519,11 +6785,222 @@
assertContainsExactly(uidCaptor.getValue(), APP2_UID);
}
+ @Test
+ public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception {
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- private MockNetworkAgent establishVpn(LinkProperties lp, int establishingUid,
- Set<UidRange> vpnRange) {
- final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN, lp);
- vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid);
+ LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_WOL_IFNAME);
+ wifiLp.setWakeOnLanSupported(false);
+
+ // Default network switch should update ifaces.
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ waitForIdle();
+
+ // ConnectivityService should have changed the WakeOnLanSupported to true
+ wifiLp.setWakeOnLanSupported(true);
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ }
+
+ @Test
+ public void testLegacyExtraInfoSentToNetworkMonitor() throws Exception {
+ class TestNetworkAgent extends NetworkAgent {
+ TestNetworkAgent(Context context, Looper looper, NetworkAgentConfig config) {
+ super(context, looper, "MockAgent", new NetworkCapabilities(),
+ new LinkProperties(), 40 , config, null /* provider */);
+ }
+ }
+ final NetworkAgent naNoExtraInfo = new TestNetworkAgent(
+ mServiceContext, mCsHandlerThread.getLooper(), new NetworkAgentConfig());
+ naNoExtraInfo.register();
+ verify(mNetworkStack).makeNetworkMonitor(any(), isNull(String.class), any());
+ naNoExtraInfo.unregister();
+
+ reset(mNetworkStack);
+ final NetworkAgentConfig config =
+ new NetworkAgentConfig.Builder().setLegacyExtraInfo("legacyinfo").build();
+ final NetworkAgent naExtraInfo = new TestNetworkAgent(
+ mServiceContext, mCsHandlerThread.getLooper(), config);
+ naExtraInfo.register();
+ verify(mNetworkStack).makeNetworkMonitor(any(), eq("legacyinfo"), any());
+ naExtraInfo.unregister();
+ }
+
+ private void setupLocationPermissions(
+ int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdk;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+ if (op != null) {
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ if (perm != null) {
+ mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+ }
+ }
+
+ private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+ final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
+
+ return mService
+ .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
+ .getOwnerUid();
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
+ // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
+ setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
+ // Test that even with fine location permission, not being the owner leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
+ // Test that not having fine location permission leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+ throws Exception {
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+ mMockVpn.setVpnType(vpnType);
+
+ final VpnInfo vpnInfo = new VpnInfo();
+ vpnInfo.ownerUid = vpnOwnerUid;
+ mMockVpn.setVpnInfo(vpnInfo);
+ }
+
+ private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+ throws Exception {
+ setupConnectionOwnerUid(vpnOwnerUid, vpnType);
+
+ // Test as VPN app
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED);
+ }
+
+ private ConnectionInfo getTestConnectionInfo() throws Exception {
+ return new ConnectionInfo(
+ IPPROTO_TCP,
+ new InetSocketAddress(InetAddresses.parseNumericAddress("1.2.3.4"), 1234),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("2.3.4.5"), 2345));
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidPlatformVpn() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM);
+
+ try {
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ fail("Expected SecurityException for non-VpnService app");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceWrongUser() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE);
+
+ try {
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ fail("Expected SecurityException for non-VpnService app");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceDoesNotThrow() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceMainlineNetworkStackDoesNotThrow()
+ throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ }
+
+ private TestNetworkAgentWrapper establishVpn(
+ LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception {
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
+ vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(vpnRange);
@@ -6532,14 +7009,6 @@
return vpnNetworkAgent;
}
- private void assertContainsExactly(int[] actual, int... expected) {
- int[] sortedActual = Arrays.copyOf(actual, actual.length);
- int[] sortedExpected = Arrays.copyOf(expected, expected.length);
- Arrays.sort(sortedActual);
- Arrays.sort(sortedExpected);
- assertArrayEquals(sortedExpected, sortedActual);
- }
-
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
@@ -6556,4 +7025,407 @@
UserHandle.getAppId(uid));
return packageInfo;
}
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackInvalidRequest() throws Exception {
+ final NetworkRequest request =
+ new NetworkRequest(
+ new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
+ try {
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+ fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) {
+ assertEquals(route.getDestination().toString(), parcel.destination);
+ assertEquals(route.getInterface(), parcel.ifName);
+ assertEquals(route.getMtu(), parcel.mtu);
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ assertEquals(route.getGateway().getHostAddress(), parcel.nextHop);
+ } else {
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop);
+ break;
+ case RouteInfo.RTN_THROW:
+ assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop);
+ break;
+ default:
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ break;
+ }
+ }
+
+ private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
+ for (int i = 0; i < routes.length; i++) {
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
+ }
+ }
+
+ private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception {
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId),
+ captor.capture());
+ for (int i = 0; i < routes.length; i++) {
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
+ }
+ }
+
+ @Test
+ public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest wifiRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ verify(mConnectivityDiagnosticsCallback).asBinder();
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
+
+ mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
+ verify(mIBinder, timeout(TIMEOUT_MS))
+ .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
+ verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest wifiRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ verify(mConnectivityDiagnosticsCallback).asBinder();
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
+
+ // Register the same callback again
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+ assertTrue(
+ "NetworkStack permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ assertFalse(
+ "Mismatched uid/package name should not pass the location permission check",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ assertFalse(
+ "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+ final Network network = new Network(NET_ID);
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, network, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
+ // active
+ final VpnInfo info = new VpnInfo();
+ info.ownerUid = Process.myUid();
+ info.vpnIface = "interface";
+ mMockVpn.setVpnInfo(info);
+ mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+ assertTrue(
+ "Active VPN permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+
+ mMockVpn.overrideUnderlyingNetworks(null);
+ assertFalse(
+ "VPN shouldn't receive callback on non-underlying network",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithoutUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setAdministratorUids(new int[] {Process.myUid()});
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, 0, mServiceContext, null, null,
+ mService, null, null, null, 0, INVALID_UID);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
+ mMockVpn.disconnect();
+ assertTrue(
+ "NetworkCapabilities administrator uid permission not applied",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setOwnerUid(Process.myUid());
+ nc.setAdministratorUids(new int[] {Process.myUid()});
+ final NetworkAgentInfo naiWithUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, nc, 0, mServiceContext, null, null,
+ mService, null, null, null, 0, INVALID_UID);
+
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ // Use wrong pid and uid
+ assertFalse(
+ "Permissions allowed when they shouldn't be granted",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithUid,
+ mContext.getOpPackageName()));
+ }
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
+ throws Exception {
+ // Set up the Network, which leads to a ConnectivityReport being cached for the network.
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mConnectivityDiagnosticsCallback)
+ .onConnectivityReportAvailable(argThat(report -> {
+ return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName())
+ && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR);
+ }));
+ }
+
+ private void setUpConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Connect the cell agent verify that it notifies TestNetworkCallback that it is available
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
+ throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Verify onConnectivityReport fired
+ verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
+ argThat(report -> {
+ final NetworkCapabilities nc = report.getNetworkCapabilities();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().length == 0
+ && nc.getOwnerUid() == Process.INVALID_UID;
+ }));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the
+ // cellular network agent
+ mCellNetworkAgent.notifyDataStallSuspected();
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Verify onDataStallSuspected fired
+ verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(
+ argThat(report -> {
+ final NetworkCapabilities nc = report.getNetworkCapabilities();
+ return nc.getUids() == null
+ && nc.getAdministratorUids().length == 0
+ && nc.getOwnerUid() == Process.INVALID_UID;
+ }));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ final Network n = mCellNetworkAgent.getNetwork();
+ final boolean hasConnectivity = true;
+ mService.reportNetworkConnectivity(n, hasConnectivity);
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Verify onNetworkConnectivityReported fired
+ verify(mConnectivityDiagnosticsCallback)
+ .onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
+
+ final boolean noConnectivity = false;
+ mService.reportNetworkConnectivity(n, noConnectivity);
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback)
+ .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+ }
+
+ @Test
+ public void testRouteAddDeleteUpdate() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, networkCallback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ reset(mMockNetd);
+ mCellNetworkAgent.connect(false);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ final int netId = mCellNetworkAgent.getNetwork().netId;
+
+ final String iface = "rmnet_data0";
+ final InetAddress gateway = InetAddress.getByName("fe80::5678");
+ RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface);
+ RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface);
+ RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface);
+ RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface);
+ RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST,
+ 1280 /* mtu */);
+
+ // Send LinkProperties and check that we ask netd to add routes.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ lp.addRoute(direct);
+ lp.addRoute(rio1);
+ lp.addRoute(defaultRoute);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3);
+
+ assertRoutesAdded(netId, direct, rio1, defaultRoute);
+ reset(mMockNetd);
+
+ // Send updated LinkProperties and check that we ask netd to add, remove, update routes.
+ assertTrue(lp.getRoutes().contains(defaultRoute));
+ lp.removeRoute(rio1);
+ lp.addRoute(rio2);
+ lp.addRoute(defaultWithMtu);
+ // Ensure adding the same route with a different MTU replaces the previous route.
+ assertFalse(lp.getRoutes().contains(defaultRoute));
+ assertTrue(lp.getRoutes().contains(defaultWithMtu));
+
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ x -> x.getRoutes().contains(rio2));
+
+ assertRoutesRemoved(netId, rio1);
+ assertRoutesAdded(netId, rio2);
+
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture());
+ assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue());
+
+
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 71b72b8..529d03c 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -46,6 +46,7 @@
import android.net.Network;
import android.net.NetworkUtils;
import android.os.Binder;
+import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
import android.system.Os;
import android.test.mock.MockContext;
@@ -135,6 +136,7 @@
};
INetd mMockNetd;
+ INetworkManagementService mNetworkManager;
PackageManager mMockPkgMgr;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
@@ -160,9 +162,10 @@
@Before
public void setUp() throws Exception {
mMockNetd = mock(INetd.class);
+ mNetworkManager = mock(INetworkManagementService.class);
mMockPkgMgr = mock(PackageManager.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(mMockContext, mNetworkManager, mMockIpSecSrvConfig);
// Injecting mock netd
when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -544,6 +547,16 @@
@Test
public void testApplyTransportModeTransform() throws Exception {
+ verifyApplyTransportModeTransformCommon(false);
+ }
+
+ @Test
+ public void testApplyTransportModeTransformReleasedSpi() throws Exception {
+ verifyApplyTransportModeTransformCommon(true);
+ }
+
+ public void verifyApplyTransportModeTransformCommon(
+ boolean closeSpiBeforeApply) throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
addAuthAndCryptToIpSecConfig(ipSecConfig);
@@ -551,6 +564,39 @@
IpSecTransformResponse createTransformResp =
mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ if (closeSpiBeforeApply) {
+ mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+ }
+
+ Socket socket = new Socket();
+ socket.bind(null);
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+
+ int resourceId = createTransformResp.resourceId;
+ mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
+
+ verify(mMockNetd)
+ .ipSecApplyTransportModeTransform(
+ eq(pfd),
+ eq(mUid),
+ eq(IpSecManager.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI));
+ }
+
+ @Test
+ public void testApplyTransportModeTransformWithClosedSpi() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+
+ // Close SPI record
+ mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+
Socket socket = new Socket();
socket.bind(null);
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
@@ -609,6 +655,7 @@
anyInt(),
anyInt(),
anyInt());
+ verify(mNetworkManager).setInterfaceUp(createTunnelResp.interfaceName);
}
@Test
@@ -656,6 +703,15 @@
@Test
public void testApplyTunnelModeTransform() throws Exception {
+ verifyApplyTunnelModeTransformCommon(false);
+ }
+
+ @Test
+ public void testApplyTunnelModeTransformReleasedSpi() throws Exception {
+ verifyApplyTunnelModeTransformCommon(true);
+ }
+
+ public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply) throws Exception {
IpSecConfig ipSecConfig = new IpSecConfig();
ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
@@ -666,6 +722,49 @@
IpSecTunnelInterfaceResponse createTunnelResp =
createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+ if (closeSpiBeforeApply) {
+ mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+ }
+
+ int transformResourceId = createTransformResp.resourceId;
+ int tunnelResourceId = createTunnelResp.resourceId;
+ mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+ transformResourceId, "blessedPackage");
+
+ for (int selAddrFamily : ADDRESS_FAMILIES) {
+ verify(mMockNetd)
+ .ipSecUpdateSecurityPolicy(
+ eq(mUid),
+ eq(selAddrFamily),
+ eq(IpSecManager.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI),
+ anyInt(), // iKey/oKey
+ anyInt(), // mask
+ eq(tunnelResourceId));
+ }
+
+ ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+ verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+ }
+
+
+ @Test
+ public void testApplyTunnelModeTransformWithClosedSpi() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+ IpSecTunnelInterfaceResponse createTunnelResp =
+ createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+ // Close SPI record
+ mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+
int transformResourceId = createTransformResp.resourceId;
int tunnelResourceId = createTunnelResp.resourceId;
mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 22a2c94..788e4ef 100644
--- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -61,7 +62,8 @@
public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(
+ mMockContext, mock(INetworkManagementService.class), mMockIpSecSrvConfig);
}
private void assertResourceState(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 4a35015..536e983 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -42,6 +42,7 @@
import android.net.IpSecSpiResponse;
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
+import android.os.INetworkManagementService;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.system.ErrnoException;
@@ -115,6 +116,7 @@
}
Context mMockContext;
+ INetworkManagementService mMockNetworkManager;
INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
@@ -122,9 +124,10 @@
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
+ mMockNetworkManager = mock(INetworkManagementService.class);
mMockNetd = mock(INetd.class);
mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ mIpSecService = new IpSecService(mMockContext, mMockNetworkManager, mMockIpSecSrvConfig);
// Injecting mock netd
when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -132,7 +135,7 @@
@Test
public void testIpSecServiceCreate() throws InterruptedException {
- IpSecService ipSecSrv = IpSecService.create(mMockContext);
+ IpSecService ipSecSrv = IpSecService.create(mMockContext, mMockNetworkManager);
assertNotNull(ipSecSrv);
}
@@ -604,8 +607,8 @@
@Test
public void testOpenUdpEncapSocketTagsSocket() throws Exception {
IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
- IpSecService testIpSecService =
- new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
+ IpSecService testIpSecService = new IpSecService(
+ mMockContext, mMockNetworkManager, mMockIpSecSrvConfig, mockTagger);
IpSecUdpEncapResponse udpEncapResp =
testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index f045369..42d4cf3 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -18,6 +18,7 @@
import android.net.ConnectivityManager.TYPE_ETHERNET
import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_MOBILE_SUPL
import android.net.ConnectivityManager.TYPE_WIFI
import android.net.ConnectivityManager.TYPE_WIMAX
import android.net.NetworkInfo.DetailedState.CONNECTED
@@ -46,7 +47,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
class LegacyTypeTrackerTest {
- private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET)
+ private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL)
private val mMockService = mock(ConnectivityService::class.java).apply {
doReturn(false).`when`(this).isDefaultNetwork(any())
@@ -70,6 +71,26 @@
}
@Test
+ fun testSupl() {
+ val mobileNai = mock(NetworkAgentInfo::class.java)
+ mTracker.add(TYPE_MOBILE, mobileNai)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE)
+ reset(mMockService)
+ mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
+ reset(mMockService)
+ mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
+ reset(mMockService)
+ mTracker.add(TYPE_MOBILE_SUPL, mobileNai)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL)
+ reset(mMockService)
+ mTracker.remove(mobileNai, false)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL)
+ verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE)
+ }
+
+ @Test
fun testAddNetwork() {
val mobileNai = mock(NetworkAgentInfo::class.java)
val wifiNai = mock(NetworkAgentInfo::class.java)
diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/tests/net/java/com/android/server/NetIdManagerTest.kt
new file mode 100644
index 0000000..045f89f
--- /dev/null
+++ b/tests/net/java/com/android/server/NetIdManagerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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 com.android.server
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.server.NetIdManager.MIN_NET_ID
+import com.android.testutils.ExceptionUtils.ThrowingRunnable
+import com.android.testutils.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetIdManagerTest {
+ @Test
+ fun testReserveReleaseNetId() {
+ val manager = NetIdManager(MIN_NET_ID + 4)
+ assertEquals(MIN_NET_ID, manager.reserveNetId())
+ assertEquals(MIN_NET_ID + 1, manager.reserveNetId())
+ assertEquals(MIN_NET_ID + 2, manager.reserveNetId())
+ assertEquals(MIN_NET_ID + 3, manager.reserveNetId())
+
+ manager.releaseNetId(MIN_NET_ID + 1)
+ manager.releaseNetId(MIN_NET_ID + 3)
+ // IDs only loop once there is no higher ID available
+ assertEquals(MIN_NET_ID + 4, manager.reserveNetId())
+ assertEquals(MIN_NET_ID + 1, manager.reserveNetId())
+ assertEquals(MIN_NET_ID + 3, manager.reserveNetId())
+ assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() })
+ manager.releaseNetId(MIN_NET_ID + 5)
+ // Still no ID available: MIN_NET_ID + 5 was not reserved
+ assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() })
+ manager.releaseNetId(MIN_NET_ID + 2)
+ // Throwing an exception still leaves the manager in a working state
+ assertEquals(MIN_NET_ID + 2, manager.reserveNetId())
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 8fa0ab9..508b5cd 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -18,35 +18,56 @@
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+import static com.android.testutils.MiscAssertsKt.assertContainsExactly;
+import static com.android.testutils.MiscAssertsKt.assertContainsStringsExactly;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.IDnsResolver;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ResolverOptionsParcel;
+import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.shared.PrivateDnsConfig;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.MessageUtils;
import com.android.internal.util.test.FakeSettingsProvider;
+import libcore.net.InetAddressUtils;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -66,8 +87,11 @@
static final int TEST_NETID = 100;
static final int TEST_NETID_ALTERNATE = 101;
static final int TEST_NETID_UNTRACKED = 102;
- final boolean IS_DEFAULT = true;
- final boolean NOT_DEFAULT = false;
+ static final int TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
+ static final int TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
+ static final int TEST_DEFAULT_MIN_SAMPLES = 8;
+ static final int TEST_DEFAULT_MAX_SAMPLES = 64;
+ static final int[] TEST_TRANSPORT_TYPES = {TRANSPORT_WIFI, TRANSPORT_VPN};
DnsManager mDnsManager;
MockContentResolver mContentResolver;
@@ -76,6 +100,36 @@
@Mock IDnsResolver mMockDnsResolver;
@Mock MockableSystemProperties mSystemProperties;
+ private void assertResolverOptionsEquals(
+ @NonNull ResolverOptionsParcel actual,
+ @NonNull ResolverOptionsParcel expected) {
+ assertEquals(actual.hosts, expected.hosts);
+ assertEquals(actual.tcMode, expected.tcMode);
+ assertEquals(actual.enforceDnsUid, expected.enforceDnsUid);
+ assertFieldCountEquals(3, ResolverOptionsParcel.class);
+ }
+
+ private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual,
+ @NonNull ResolverParamsParcel expected) {
+ assertEquals(actual.netId, expected.netId);
+ assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds);
+ assertEquals(actual.successThreshold, expected.successThreshold);
+ assertEquals(actual.minSamples, expected.minSamples);
+ assertEquals(actual.maxSamples, expected.maxSamples);
+ assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec);
+ assertEquals(actual.retryCount, expected.retryCount);
+ assertContainsStringsExactly(actual.servers, expected.servers);
+ assertContainsStringsExactly(actual.domains, expected.domains);
+ assertEquals(actual.tlsName, expected.tlsName);
+ assertContainsStringsExactly(actual.tlsServers, expected.tlsServers);
+ assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints);
+ assertEquals(actual.caCertificate, expected.caCertificate);
+ assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs);
+ assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions);
+ assertContainsExactly(actual.transportTypes, expected.transportTypes);
+ assertFieldCountEquals(16, ResolverParamsParcel.class);
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -103,8 +157,13 @@
lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
// Send a validation event that is tracked on the alternate netId
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT);
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
+ mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp);
+ mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
InetAddress.parseNumericAddress("4.4.4.4"), "", true));
@@ -135,7 +194,10 @@
InetAddress.parseNumericAddress("6.6.6.6"),
InetAddress.parseNumericAddress("2001:db8:66:66::1")
}));
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
fixedLp = new LinkProperties(lp);
mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
assertTrue(fixedLp.isPrivateDnsActive());
@@ -168,7 +230,10 @@
// be tracked.
LinkProperties lp = new LinkProperties();
lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -179,7 +244,10 @@
// Validation event has untracked netId
mDnsManager.updatePrivateDns(new Network(TEST_NETID),
mDnsManager.getPrivateDnsConfig());
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -225,7 +293,10 @@
Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
mDnsManager.updatePrivateDns(new Network(TEST_NETID),
mDnsManager.getPrivateDnsConfig());
- mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -258,4 +329,102 @@
assertEquals("strictmode.com", cfgStrict.hostname);
assertEquals(new InetAddress[0], cfgStrict.ips);
}
+
+ @Test
+ public void testSendDnsConfiguration() throws Exception {
+ reset(mMockDnsResolver);
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(TEST_IFACENAME);
+ lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+ lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
+ mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+ mDnsManager.flushVmDnsCache();
+
+ final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor =
+ ArgumentCaptor.forClass(ResolverParamsParcel.class);
+ verify(mMockDnsResolver, times(1)).setResolverConfiguration(
+ resolverParamsParcelCaptor.capture());
+ final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue();
+ final ResolverParamsParcel expectedParams = new ResolverParamsParcel();
+ expectedParams.netId = TEST_NETID;
+ expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS;
+ expectedParams.successThreshold = TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
+ expectedParams.minSamples = TEST_DEFAULT_MIN_SAMPLES;
+ expectedParams.maxSamples = TEST_DEFAULT_MAX_SAMPLES;
+ expectedParams.servers = new String[]{"3.3.3.3", "4.4.4.4"};
+ expectedParams.domains = new String[]{};
+ expectedParams.tlsName = "";
+ expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"};
+ expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
+ expectedParams.resolverOptions = new ResolverOptionsParcel();
+ assertResolverParamsEquals(actualParams, expectedParams);
+ }
+
+ @Test
+ public void testTransportTypesEqual() throws Exception {
+ SparseArray<String> ncTransTypes = MessageUtils.findMessageNames(
+ new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" });
+ SparseArray<String> dnsTransTypes = MessageUtils.findMessageNames(
+ new Class[] { IDnsResolver.class }, new String[]{ "TRANSPORT_" });
+ assertEquals(0, MIN_TRANSPORT);
+ assertEquals(MAX_TRANSPORT + 1, ncTransTypes.size());
+ // TRANSPORT_UNKNOWN in IDnsResolver is defined to -1 and only for resolver.
+ assertEquals("TRANSPORT_UNKNOWN", dnsTransTypes.get(-1));
+ assertEquals(ncTransTypes.size(), dnsTransTypes.size() - 1);
+ for (int i = MIN_TRANSPORT; i < MAX_TRANSPORT; i++) {
+ String name = ncTransTypes.get(i, null);
+ assertNotNull("Could not find NetworkCapabilies.TRANSPORT_* constant equal to "
+ + i, name);
+ assertEquals(name, dnsTransTypes.get(i));
+ }
+ }
+
+ @Test
+ public void testGetPrivateDnsConfigForNetwork() throws Exception {
+ final Network network = new Network(TEST_NETID);
+ final InetAddress dnsAddr = InetAddressUtils.parseNumericAddress("3.3.3.3");
+ final InetAddress[] tlsAddrs = new InetAddress[]{
+ InetAddressUtils.parseNumericAddress("6.6.6.6"),
+ InetAddressUtils.parseNumericAddress("2001:db8:66:66::1")
+ };
+ final String tlsName = "strictmode.com";
+ LinkProperties lp = new LinkProperties();
+ lp.addDnsServer(dnsAddr);
+
+ // The PrivateDnsConfig map is empty, so the default PRIVATE_DNS_OFF is returned.
+ PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
+ assertFalse(privateDnsCfg.useTls);
+ assertEquals("", privateDnsCfg.hostname);
+ assertEquals(new InetAddress[0], privateDnsCfg.ips);
+
+ // An entry with default PrivateDnsConfig is added to the PrivateDnsConfig map.
+ mDnsManager.updatePrivateDns(network, mDnsManager.getPrivateDnsConfig());
+ mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, dnsAddr, "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
+ assertTrue(privateDnsCfg.useTls);
+ assertEquals("", privateDnsCfg.hostname);
+ assertEquals(new InetAddress[0], privateDnsCfg.ips);
+
+ // The original entry is overwritten by a new PrivateDnsConfig.
+ mDnsManager.updatePrivateDns(network, new PrivateDnsConfig(tlsName, tlsAddrs));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
+ assertTrue(privateDnsCfg.useTls);
+ assertEquals(tlsName, privateDnsCfg.hostname);
+ assertEquals(tlsAddrs, privateDnsCfg.ips);
+
+ // The network is removed, so the PrivateDnsConfig map becomes empty again.
+ mDnsManager.removeNetwork(network);
+ privateDnsCfg = mDnsManager.getPrivateDnsConfig(network);
+ assertFalse(privateDnsCfg.useTls);
+ assertEquals("", privateDnsCfg.hostname);
+ assertEquals(new InetAddress[0], privateDnsCfg.ips);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 142769f..aafa18a 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -36,9 +36,9 @@
import android.net.INetd;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
+import android.net.NetworkProvider;
+import android.os.Binder;
import android.os.INetworkManagementService;
import android.text.format.DateUtils;
@@ -74,7 +74,6 @@
@Mock INetd mNetd;
@Mock INetworkManagementService mNMS;
@Mock Context mCtx;
- @Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
@@ -355,8 +354,8 @@
caps.addCapability(0);
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
- caps, 50, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
- NetworkFactory.SerialNumber.NONE);
+ caps, 50, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
+ NetworkProvider.ID_NONE, Binder.getCallingUid());
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index b783467..de1028c 100644
--- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -51,6 +51,7 @@
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -229,7 +230,7 @@
verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
// Simulate callback after capability changes
- final NetworkCapabilities capabilities = new NetworkCapabilities()
+ NetworkCapabilities capabilities = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(TRANSPORT_CELLULAR)
.setNetworkSpecifier(new StringNetworkSpecifier("234"));
@@ -239,6 +240,19 @@
networkCallback.getValue().onCapabilitiesChanged(
TEST_NETWORK,
capabilities);
+
+ // make sure it also works with the new introduced TelephonyNetworkSpecifier
+ capabilities = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(234).build());
+ if (!roaming) {
+ capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
+ networkCallback.getValue().onCapabilitiesChanged(
+ TEST_NETWORK,
+ capabilities);
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index b709af1..5046b65 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -33,8 +33,8 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkAgentConfig;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.test.TestLooper;
@@ -58,12 +58,13 @@
static final String BASE_IFACE = "test0";
static final String STACKED_IFACE = "v4-test0";
+ static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64");
static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
static final String NAT64_PREFIX = "64:ff9b::/96";
+ static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96";
static final int NETID = 42;
@Mock ConnectivityService mConnectivity;
- @Mock NetworkMisc mMisc;
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock INetworkManagementService mNms;
@@ -72,6 +73,7 @@
TestLooper mLooper;
Handler mHandler;
+ NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
Nat464Xlat makeNat464Xlat() {
return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
@@ -81,6 +83,14 @@
};
}
+ private void markNetworkConnected() {
+ mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+ }
+
+ private void markNetworkDisconnected() {
+ mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", "");
+ }
+
@Before
public void setUp() throws Exception {
mLooper = new TestLooper();
@@ -92,8 +102,9 @@
mNai.linkProperties.setInterfaceName(BASE_IFACE);
mNai.networkInfo = new NetworkInfo(null);
mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ markNetworkConnected();
when(mNai.connService()).thenReturn(mConnectivity);
- when(mNai.netMisc()).thenReturn(mMisc);
+ when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
when(mNai.handler()).thenReturn(mHandler);
when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -104,7 +115,7 @@
String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
}
@@ -113,7 +124,7 @@
String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
}
@@ -139,7 +150,7 @@
for (NetworkInfo.DetailedState state : supportedDetailedStates) {
mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
- mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX));
assertRequiresClat(false, mNai);
assertShouldStartClat(false, mNai);
@@ -151,11 +162,11 @@
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
- mMisc.skip464xlat = true;
+ mAgentConfig.skip464xlat = true;
assertRequiresClat(false, mNai);
assertShouldStartClat(false, mNai);
- mMisc.skip464xlat = false;
+ mAgentConfig.skip464xlat = false;
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
@@ -176,12 +187,21 @@
}
}
- @Test
- public void testNormalStartAndStop() throws Exception {
+ private void makeClatUnnecessary(boolean dueToDisconnect) {
+ if (dueToDisconnect) {
+ markNetworkDisconnected();
+ } else {
+ mNai.linkProperties.addLinkAddress(ADDR);
+ }
+ }
+
+ private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
- nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+ mNai.linkProperties.addLinkAddress(V6ADDR);
+
+ nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
// Start clat.
nat.start();
@@ -200,6 +220,7 @@
assertRunning(nat);
// Stop clat (Network disconnects, IPv4 addr appears, ...).
+ makeClatUnnecessary(dueToDisconnect);
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -217,12 +238,24 @@
verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
}
+ @Test
+ public void testNormalStartAndStopDueToDisconnect() throws Exception {
+ checkNormalStartAndStop(true);
+ }
+
+ @Test
+ public void testNormalStartAndStopDueToIpv4Addr() throws Exception {
+ checkNormalStartAndStop(false);
+ }
+
private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
InOrder inOrder = inOrder(mNetd, mConnectivity);
- nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+ mNai.linkProperties.addLinkAddress(V6ADDR);
+
+ nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
nat.start();
@@ -309,7 +342,7 @@
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
- nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+ nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
nat.start();
@@ -344,11 +377,12 @@
verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
}
- @Test
- public void testStopBeforeClatdStarts() throws Exception {
+ private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
Nat464Xlat nat = makeNat464Xlat();
- nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+ mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+
+ nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
nat.start();
@@ -356,6 +390,7 @@
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ makeClatUnnecessary(dueToDisconnect);
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -377,10 +412,21 @@
}
@Test
- public void testStopAndClatdNeverStarts() throws Exception {
+ public void testStopDueToDisconnectBeforeClatdStarts() throws Exception {
+ checkStopBeforeClatdStarts(true);
+ }
+
+ @Test
+ public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception {
+ checkStopBeforeClatdStarts(false);
+ }
+
+ private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
Nat464Xlat nat = makeNat464Xlat();
- nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+ mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+
+ nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
nat.start();
@@ -388,6 +434,7 @@
verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ makeClatUnnecessary(dueToDisconnect);
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -398,6 +445,57 @@
verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
}
+ @Test
+ public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception {
+ checkStopAndClatdNeverStarts(true);
+ }
+
+ @Test
+ public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception {
+ checkStopAndClatdNeverStarts(false);
+ }
+
+ @Test
+ public void testNat64PrefixPreference() throws Exception {
+ final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
+ final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
+
+ Nat464Xlat nat = makeNat464Xlat();
+
+ final LinkProperties emptyLp = new LinkProperties();
+ LinkProperties fixedupLp;
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromDns(prefixFromDns);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromRa(prefixFromRa);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromRa(null);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromRa(prefixFromRa);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromDns(null);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+ fixedupLp = new LinkProperties();
+ nat.setNat64PrefixFromRa(null);
+ nat.fixupLinkProperties(emptyLp, fixedupLp);
+ assertEquals(null, fixedupLp.getNat64Prefix());
+ }
+
static void assertIdle(Nat464Xlat nat) {
assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
}
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index e4117b8..aef9386 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -19,8 +19,9 @@
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
+import static com.android.testutils.MiscAssertsKt.assertStringContains;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -111,15 +112,15 @@
String[] events2 = remove(listNetdEvent(), baseline);
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
assertEquals(expectedLength2, events2.length);
- assertContains(events2[0], "WakeupStats");
- assertContains(events2[0], "wlan0");
- assertContains(events2[0], "0x800");
- assertContains(events2[0], "0x86dd");
+ assertStringContains(events2[0], "WakeupStats");
+ assertStringContains(events2[0], "wlan0");
+ assertStringContains(events2[0], "0x800");
+ assertStringContains(events2[0], "0x86dd");
for (int i = 0; i < uids.length; i++) {
String got = events2[i+1];
- assertContains(got, "WakeupEvent");
- assertContains(got, "wlan0");
- assertContains(got, "uid: " + uids[i]);
+ assertStringContains(got, "WakeupEvent");
+ assertStringContains(got, "wlan0");
+ assertStringContains(got, "uid: " + uids[i]);
}
int uid = 20000;
@@ -131,13 +132,13 @@
String[] events3 = remove(listNetdEvent(), baseline);
int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
assertEquals(expectedLength3, events3.length);
- assertContains(events2[0], "WakeupStats");
- assertContains(events2[0], "wlan0");
+ assertStringContains(events2[0], "WakeupStats");
+ assertStringContains(events2[0], "wlan0");
for (int i = 1; i < expectedLength3; i++) {
String got = events3[i];
- assertContains(got, "WakeupEvent");
- assertContains(got, "wlan0");
- assertContains(got, "uid: " + uid);
+ assertStringContains(got, "WakeupEvent");
+ assertStringContains(got, "wlan0");
+ assertStringContains(got, "uid: " + uid);
}
uid = 45678;
@@ -145,9 +146,9 @@
String[] events4 = remove(listNetdEvent(), baseline);
String lastEvent = events4[events4.length - 1];
- assertContains(lastEvent, "WakeupEvent");
- assertContains(lastEvent, "wlan0");
- assertContains(lastEvent, "uid: " + uid);
+ assertStringContains(lastEvent, "WakeupEvent");
+ assertStringContains(lastEvent, "wlan0");
+ assertStringContains(lastEvent, "uid: " + uid);
}
@Test
@@ -529,10 +530,6 @@
return buffer.toString().split("\\n");
}
- static void assertContains(String got, String want) {
- assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
- }
-
static <T> T[] remove(T[] array, T[] filtered) {
List<T> c = Arrays.asList(filtered);
int next = 0;
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 9580763..47db5d4 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -40,6 +41,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import org.junit.Before;
@@ -60,12 +62,19 @@
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
+ static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
static {
CELL_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
CELL_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Set the underyling network to wifi.
+ VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
+ VPN_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ VPN_CAPABILITIES.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
}
@Mock Context mCtx;
@@ -75,6 +84,7 @@
@Mock NotificationManager mNotificationManager;
@Mock NetworkAgentInfo mWifiNai;
@Mock NetworkAgentInfo mCellNai;
+ @Mock NetworkAgentInfo mVpnNai;
@Mock NetworkInfo mNetworkInfo;
ArgumentCaptor<Notification> mCaptor;
@@ -88,6 +98,9 @@
mWifiNai.networkInfo = mNetworkInfo;
mCellNai.networkCapabilities = CELL_CAPABILITIES;
mCellNai.networkInfo = mNetworkInfo;
+ mVpnNai.networkCapabilities = VPN_CAPABILITIES;
+ mVpnNai.networkInfo = mNetworkInfo;
+ doReturn(true).when(mVpnNai).isVPN();
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageManager()).thenReturn(mPm);
when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo());
@@ -97,6 +110,35 @@
mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
}
+ private void verifyTitleByNetwork(final int id, final NetworkAgentInfo nai, final int title) {
+ final String tag = NetworkNotificationManager.tagFor(id);
+ mManager.showNotification(id, PRIVATE_DNS_BROKEN, nai, null, null, true);
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any(), any());
+ final int transportType = NetworkNotificationManager.approximateTransportType(nai);
+ if (transportType == NetworkCapabilities.TRANSPORT_WIFI) {
+ verify(mResources, times(1)).getString(title, eq(any()));
+ } else {
+ verify(mResources, times(1)).getString(title);
+ }
+ verify(mResources, times(1)).getString(R.string.private_dns_broken_detailed);
+ }
+
+ @Test
+ public void testTitleOfPrivateDnsBroken() {
+ // Test the title of mobile data.
+ verifyTitleByNetwork(100, mCellNai, R.string.mobile_no_internet);
+ reset(mResources);
+
+ // Test the title of wifi.
+ verifyTitleByNetwork(101, mWifiNai, R.string.wifi_no_internet);
+ reset(mResources);
+
+ // Test the title of other networks.
+ verifyTitleByNetwork(102, mVpnNai, R.string.other_networks_no_internet);
+ reset(mResources);
+ }
+
@Test
public void testNotificationsShownAndCleared() {
final int NETWORK_ID_BASE = 100;
@@ -196,20 +238,6 @@
}
@Test
- public void testSameLevelNotifications() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
-
- mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(1))
- .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
- mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(1))
- .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
- }
-
- @Test
public void testClearNotificationByType() {
final int id = 101;
final String tag = NetworkNotificationManager.tagFor(id);
@@ -217,31 +245,25 @@
// clearNotification(int id, NotificationType notifyType) will check if given type is equal
// to previous type or not. If they are equal then clear the notification; if they are not
// equal then return.
-
- mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+ mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false);
verify(mNotificationManager, times(1))
- .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+ .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any());
- // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification
+ // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification
// should be cleared.
- mManager.clearNotification(id, LOGGED_IN);
+ mManager.clearNotification(id, NO_INTERNET);
verify(mNotificationManager, times(1))
- .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any());
+ .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any());
- mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(2))
- .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
- // LOST_INTERNET notification popup after LOGGED_IN notification.
- mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+ // SIGN_IN is popped-up.
+ mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
verify(mNotificationManager, times(1))
- .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
+ .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any());
- // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification
- // shouldn't be cleared.
- mManager.clearNotification(id, LOGGED_IN);
- // LOST_INTERNET shouldn't be cleared.
+ // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be
+ // cleared.
+ mManager.clearNotification(id, PARTIAL_CONNECTIVITY);
verify(mNotificationManager, never())
- .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any());
+ .cancelAsUser(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId), any());
}
}
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..86c9116
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.connectivity
+
+import android.net.NetworkRequest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkRankerTest {
+ private val ranker = NetworkRanker()
+
+ private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
+ doReturn(satisfy).`when`(it).satisfies(any())
+ doReturn(score).`when`(it).currentScore
+ }
+
+ @Test
+ fun testGetBestNetwork() {
+ val scores = listOf(20, 50, 90, 60, 23, 68)
+ val nais = scores.map { makeNai(true, it) }
+ val bestNetwork = nais[2] // The one with the top score
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testIgnoreNonSatisfying() {
+ val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
+ makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
+ val bestNetwork = nais[1] // Top score that's satisfying
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testNoMatch() {
+ val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
+ val someRequest = mock(NetworkRequest::class.java)
+ assertNull(ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testEmpty() {
+ val someRequest = mock(NetworkRequest::class.java)
+ assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+ }
+
+ // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
+ // network satisfying the request if multiple of them have the same score.
+ @Test
+ fun testStable() {
+ val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
+ makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+
+ val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
+ makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
+ assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 2e892e5..39f849c 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -56,7 +56,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
@@ -72,6 +71,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
import org.junit.Before;
import org.junit.Test;
@@ -265,6 +265,8 @@
assertFalse(mPermissionMonitor.hasNetworkPermission(app));
app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS);
assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+ app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL);
+ assertFalse(mPermissionMonitor.hasNetworkPermission(app));
}
@Test
@@ -274,7 +276,7 @@
PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
- assertTrue(hasRestrictedNetworkPermission(
+ assertFalse(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
@@ -283,7 +285,7 @@
assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
}
@Test
@@ -291,14 +293,14 @@
doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE));
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE));
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
}
@@ -319,7 +321,7 @@
assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
assertFalse(hasRestrictedNetworkPermission(
PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE));
}
@@ -337,7 +339,7 @@
public void testHasUseBackgroundNetworksPermission() throws Exception {
assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID);
- assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_WIFI_STATE);
+ assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL);
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
@@ -348,8 +350,9 @@
assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
- assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2,
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
CONNECTIVITY_INTERNAL);
+ assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
}
private class NetdMonitor {
@@ -632,12 +635,18 @@
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
}
- private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+ private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
throws Exception {
PackageInfo packageInfo = packageInfoWithPermissions(
REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
+ return packageInfo;
+ }
+
+ private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+ throws Exception {
+ PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
mObserver.onPackageAdded(packageName, uid);
return packageInfo;
}
@@ -688,14 +697,13 @@
}
@Test
- public void testPackageUpdate() throws Exception {
+ public void testPackageRemoveThenAdd() throws Exception {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
- // Remove and install the same package to simulate the update action
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
@@ -705,6 +713,20 @@
}
@Test
+ public void testPackageUpdate() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1});
+
+ // When updating a package, the broadcast receiver gets two broadcasts (a remove and then an
+ // add), but the observer sees only one callback (an update).
+ setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
+ mObserver.onPackageChanged(MOCK_PACKAGE1, MOCK_UID1);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ }
+
+ @Test
public void testPackageUninstallWithMultiplePackages() throws Exception {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index ce50bef..4ccf79a 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -25,14 +25,15 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.RouteInfo.RTN_UNREACHABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -43,6 +44,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,21 +60,28 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
import android.net.IpPrefix;
+import android.net.IpSecManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.VpnManager;
import android.net.VpnService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.SystemClock;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -81,6 +90,8 @@
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.server.IpSecService;
import org.junit.Before;
import org.junit.Test;
@@ -91,8 +102,6 @@
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -124,6 +133,11 @@
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final String TEST_VPN_PKG = "com.dummy.vpn";
+ private static final String TEST_VPN_SERVER = "1.2.3.4";
+ private static final String TEST_VPN_IDENTITY = "identity";
+ private static final byte[] TEST_VPN_PSK = "psk".getBytes();
+
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
@@ -147,25 +161,47 @@
@Mock private AppOpsManager mAppOps;
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
+ @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private IpSecService mIpSecService;
+ @Mock private KeyStore mKeyStore;
+ private final VpnProfile mVpnProfile;
+
+ private IpSecManager mIpSecManager;
+
+ public VpnTest() throws Exception {
+ // Build an actual VPN profile that is capable of being converted to and from an
+ // Ikev2VpnProfile
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY);
+ builder.setAuthPsk(TEST_VPN_PSK);
+ mVpnProfile = builder.build().toVpnProfile();
+ }
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mIpSecManager = new IpSecManager(mContext, mIpSecService);
+
when(mContext.getPackageManager()).thenReturn(mPackageManager);
setMockedPackages(mPackages);
- when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
+ when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
.thenReturn(mConnectivityManager);
+ when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(true);
+ when(mSystemServices.isCallerSystem()).thenReturn(true);
// Used by {@link Notification.Builder}
ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -175,6 +211,10 @@
.thenReturn(applicationInfo);
doNothing().when(mNetService).registerObserver(any());
+
+ // Deny all appops by default.
+ when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
}
@Test
@@ -251,17 +291,17 @@
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList(), mKeyStore));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -275,11 +315,11 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -288,7 +328,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -307,7 +347,8 @@
final UidRange user = UidRange.createForUser(primaryUser.id);
// Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
@@ -316,7 +357,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
// Change whitelisted app to PKGS[3].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -328,7 +370,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
// Change the VPN app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
@@ -341,7 +384,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
// Remove the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -354,7 +397,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0]);
// Add the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
}));
@@ -366,12 +410,13 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
// Try whitelisting a package with a comma, should be rejected.
- assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
+ assertFalse(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore));
// Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
// Whitelisted package should change from PGKS[1] to PKGS[2].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
- Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -396,7 +441,7 @@
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -464,12 +509,12 @@
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package.
- vpn.prepare(null, PKGS[0]);
+ vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
- vpn.prepare(null, VpnConfig.LEGACY_VPN);
+ vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
}
@@ -490,22 +535,22 @@
.thenReturn(Collections.singletonList(resInfo));
// null package name should return false
- assertFalse(vpn.isAlwaysOnPackageSupported(null));
+ assertFalse(vpn.isAlwaysOnPackageSupported(null, mKeyStore));
// Pre-N apps are not supported
appInfo.targetSdkVersion = VERSION_CODES.M;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// N+ apps are supported by default
appInfo.targetSdkVersion = VERSION_CODES.N;
- assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// Apps that opt out explicitly are not supported
appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
Bundle metaData = new Bundle();
metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
svcInfo.metaData = metaData;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
}
@Test
@@ -522,7 +567,7 @@
.cancelAsUser(anyString(), anyInt(), eq(userHandle));
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false, null);
+ vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore);
order.verify(mNotificationManager)
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
@@ -536,7 +581,7 @@
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
@@ -564,6 +609,7 @@
.addCapability(NET_CAPABILITY_NOT_METERED)
.addCapability(NET_CAPABILITY_NOT_ROAMING)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
.setLinkUpstreamBandwidthKbps(20));
setMockedNetworks(networks);
@@ -579,6 +625,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
@@ -593,6 +640,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
@@ -604,6 +652,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
@@ -615,6 +664,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
@@ -629,13 +679,365 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ }
+
+ /**
+ * The profile name should NOT change between releases for backwards compatibility
+ *
+ * <p>If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST
+ * be updated to ensure backward compatibility.
+ */
+ @Test
+ public void testGetProfileNameForPackage() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG;
+ assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ }
+
+ private Vpn createVpnAndSetupUidChecks(int... grantedOps) throws Exception {
+ return createVpnAndSetupUidChecks(primaryUser, grantedOps);
+ }
+
+ private Vpn createVpnAndSetupUidChecks(UserInfo user, int... grantedOps) throws Exception {
+ final Vpn vpn = createVpn(user.id);
+ setMockedUsers(user);
+
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(Process.myUid());
+
+ for (final int op : grantedOps) {
+ when(mAppOps.noteOpNoThrow(op, Process.myUid(), TEST_VPN_PKG))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ return vpn;
+ }
+
+ private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, int... checkedOps) {
+ assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore));
+
+ // The profile should always be stored, whether or not consent has been previously granted.
+ verify(mKeyStore)
+ .put(
+ eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)),
+ eq(mVpnProfile.encode()),
+ eq(Process.SYSTEM_UID),
+ eq(0));
+
+ for (final int checkedOp : checkedOps) {
+ verify(mAppOps).noteOpNoThrow(checkedOp, Process.myUid(), TEST_VPN_PKG);
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfileNoIpsecTunnels() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+ .thenReturn(false);
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+ fail("Expected exception due to missing feature");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileNotPreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller
+ // had neither.
+ checkProvisionVpnProfile(vpn, false /* expectedResult */,
+ AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, AppOpsManager.OP_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+ checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileTooLarge() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ final VpnProfile bigProfile = new VpnProfile("");
+ bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile, mKeyStore);
+ fail("Expected IAE due to profile size");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteVpnProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore)
+ .delete(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), eq(Process.SYSTEM_UID));
+ }
+
+ @Test
+ public void testDeleteVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetVpnProfilePrivileged() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(new VpnProfile("").encode());
+
+ vpn.getVpnProfilePrivileged(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ }
+
+ @Test
+ public void testStartVpnProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testStartVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown.
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+ }
+
+ @Test
+ public void testStartVpnProfileNotConsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected failure due to no user consent");
+ } catch (SecurityException expected) {
+ }
+
+ // Verify both appops were checked.
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+
+ // Keystore should never have been accessed.
+ verify(mKeyStore, never()).get(any());
+ }
+
+ @Test
+ public void testStartVpnProfileMissingProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected failure due to missing profile");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ verify(mKeyStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testStartVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testStopVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testSetPackageAuthorizationVpnService() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationPlatformVpn() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ }
+
+ private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps).setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutIntForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0),
+ eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id));
+ }
+
+ @Test
+ public void testSetAndStartAlwaysOnVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn(mKeyStore));
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
+ @Test
+ public void testStartLegacyVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // Dummy egress interface
+ final String egressIface = "DUMMY0";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(egressIface);
+
+ final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ InetAddresses.parseNumericAddress("192.0.2.0"), egressIface);
+ lp.addRoute(defaultRoute);
+
+ vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp);
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
}
/**
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
- return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
+ return new Vpn(Looper.myLooper(), mContext, mNetService,
+ userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
}
private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 28785f7..3aafe0b 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -41,6 +41,7 @@
static final String TEST_IFACE = "test0";
static final String TEST_IFACE2 = "test1";
static final String TUN_IFACE = "test_nss_tun0";
+ static final String TUN_IFACE2 = "test_nss_tun1";
static final int UID_RED = 1001;
static final int UID_BLUE = 1002;
@@ -107,10 +108,14 @@
assertEquals("unexpected operations", operations, entry.operations);
}
- VpnInfo createVpnInfo(String[] underlyingIfaces) {
+ static VpnInfo createVpnInfo(String[] underlyingIfaces) {
+ return createVpnInfo(TUN_IFACE, underlyingIfaces);
+ }
+
+ static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
VpnInfo info = new VpnInfo();
info.ownerUid = UID_VPN;
- info.vpnIface = TUN_IFACE;
+ info.vpnIface = vpnIface;
info.underlyingIfaces = underlyingIfaces;
return info;
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 9b4f49c..551498f 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -29,6 +29,7 @@
import static com.android.server.net.NetworkStatsCollection.multiplySafe;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -43,7 +44,6 @@
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
-import android.test.MoreAsserts;
import android.text.format.DateUtils;
import android.util.RecurrenceRule;
@@ -240,11 +240,11 @@
60 * MINUTE_IN_MILLIS, entry);
// Verify the set of relevant UIDs for each access level.
- MoreAsserts.assertEquals(new int[] { myUid },
+ assertArrayEquals(new int[] { myUid },
collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT));
- MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
+ assertArrayEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
collection.getRelevantUids(NetworkStatsAccess.Level.USER));
- MoreAsserts.assertEquals(
+ assertArrayEquals(
new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser },
collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
@@ -319,33 +319,33 @@
assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
- assertEntry(10747, 50, 16838, 55, history.getValues(i++, null));
- assertEntry(10747, 49, 16838, 54, history.getValues(i++, null));
+ assertEntry(10747, 50, 16839, 55, history.getValues(i++, null));
+ assertEntry(10747, 49, 16837, 54, history.getValues(i++, null));
assertEntry(89191, 151, 18021, 140, history.getValues(i++, null));
assertEntry(89190, 150, 18020, 139, history.getValues(i++, null));
- assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
- assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+ assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+ assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+ assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
- assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
- assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
- assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
- assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
- assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
- assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+ assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+ assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+ assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
assertEquals(history.size(), i);
// Slice from middle should be untouched
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
TIME_B + HOUR_IN_MILLIS); i = 0;
- assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
- assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+ assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
assertEquals(history.size(), i);
}
@@ -373,25 +373,25 @@
assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
// Cycle point; start data normalization
assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
- assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
+ assertEntry(7507, 0, 11762, 0, history.getValues(i++, null));
assertEntry(62309, 0, 12589, 0, history.getValues(i++, null));
assertEntry(62309, 0, 12588, 0, history.getValues(i++, null));
assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
// Anchor point; end data normalization
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+ assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+ assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
// Cycle point
- assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
- assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
- assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
- assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
- assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
- assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+ assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+ assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+ assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
assertEquals(history.size(), i);
// Slice from middle should be augmented
@@ -399,8 +399,8 @@
TIME_B + HOUR_IN_MILLIS); i = 0;
assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
assertEquals(history.size(), i);
}
@@ -427,34 +427,34 @@
assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
// Cycle point; start data normalization
- assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
- assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
+ assertEntry(15015, 0, 23527, 0, history.getValues(i++, null));
+ assertEntry(15015, 0, 23524, 0, history.getValues(i++, null));
assertEntry(124619, 0, 25179, 0, history.getValues(i++, null));
assertEntry(124618, 0, 25177, 0, history.getValues(i++, null));
assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
// Anchor point; end data normalization
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
- assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+ assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+ assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
// Cycle point
- assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
- assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
- assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
- assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
- assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
- assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+ assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+ assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+ assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+ assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+ assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+ assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
// Slice from middle should be augmented
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
TIME_B + HOUR_IN_MILLIS); i = 0;
assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
- assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
- assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+ assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+ assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
assertEquals(history.size(), i);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 7329474..e4996d9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -79,8 +79,7 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false);
- NetworkStatsFactory.updateVpnInfos(new VpnInfo[0]);
- NetworkStatsFactory.clearStackedIfaces();
+ mFactory.updateVpnInfos(new VpnInfo[0]);
}
@After
@@ -105,9 +104,9 @@
}
@Test
- public void vpnRewriteTrafficThroughItself() throws Exception {
+ public void testVpnRewriteTrafficThroughItself() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -134,10 +133,10 @@
}
@Test
- public void vpnWithClat() throws Exception {
+ public void testVpnWithClat() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
- NetworkStatsFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
+ mFactory.updateVpnInfos(vpnInfos);
+ mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -167,9 +166,9 @@
}
@Test
- public void vpnWithOneUnderlyingIface() throws Exception {
+ public void testVpnWithOneUnderlyingIface() throws Exception {
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -190,10 +189,10 @@
}
@Test
- public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
+ public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -218,10 +217,10 @@
}
@Test
- public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
+ public void testVpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -239,12 +238,12 @@
}
@Test
- public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
+ public void testVpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is duplicating traffic across both WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -265,12 +264,52 @@
}
@Test
- public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
+ public void testConcurrentVpns() throws Exception {
+ // Assume two VPNs are connected on two different network interfaces. VPN1 is using
+ // TEST_IFACE and VPN2 is using TEST_IFACE2.
+ final VpnInfo[] vpnInfos = new VpnInfo[] {
+ createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}),
+ createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})};
+ mFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN1.
+ // 700 bytes (70 packets) were sent, and 3000 bytes (300 packets) were received by UID_RED
+ // over VPN2.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN1.
+ // 250 bytes (25 packets) were sent, and 500 bytes (50 packets) were received by UID_BLUE
+ // over VPN2.
+ // VPN1 sent 1650 bytes (150 packets), and received 3300 (300 packets) over TEST_IFACE.
+ // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
+ // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+ // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
+ // VPN2 sent 1045 bytes (95 packets), and received 3850 (350 packets) over TEST_IFACE2.
+ // Of 1045 bytes sent over Cell, expect 700 bytes attributed to UID_RED, 250 bytes
+ // attributed to UID_BLUE, and 95 bytes attributed to UID_VPN.
+ // Of 3850 bytes received over Cell, expect 3000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 350 bytes attributed to UID_VPN.
+ final NetworkStats tunStats =
+ parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE2, UID_RED, 3000L, 300L, 700L, 70L);
+ assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 250L, 25L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L);
+ assertValues(tunStats, TEST_IFACE2, UID_VPN, 350L, 0L, 95L, 0L);
+ }
+
+ @Test
+ public void testVpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -292,12 +331,12 @@
}
@Test
- public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
+ public void testVpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface:
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
@@ -315,11 +354,11 @@
}
@Test
- public void vpnWithIncorrectUnderlyingIface() throws Exception {
+ public void testVpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateVpnInfos(vpnInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -383,7 +422,7 @@
@Test
public void testDoubleClatAccountingSimple() throws Exception {
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
// xt_qtaguid_with_clat_simple is a synthetic file that simulates
// - 213 received 464xlat packets of size 200 bytes
@@ -398,7 +437,7 @@
@Test
public void testDoubleClatAccounting() throws Exception {
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
assertEquals(42, stats.size());
@@ -407,7 +446,7 @@
assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);
assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -419,8 +458,6 @@
assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0);
-
- NetworkStatsFactory.clearStackedIfaces();
}
@Test
@@ -431,24 +468,20 @@
long appRxBytesAfter = 439237478L;
assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore);
- long rootRxBytesBefore = 1394011L;
- long rootRxBytesAfter = 1398634L;
- assertEquals("UID 0 traffic should be ~0", 4623, rootRxBytesAfter - rootRxBytesBefore);
+ long rootRxBytes = 330187296L;
- NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+ mFactory.noteStackedIface("v4-wlan0", "wlan0");
NetworkStats stats;
// Stats snapshot before the download
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
// Stats snapshot after the download
stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
-
- NetworkStatsFactory.clearStackedIfaces();
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
}
/**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index f21a7dd..a6f7a36 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -28,8 +28,6 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -55,6 +53,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
+import com.android.testutils.HandlerUtilsKt;
import org.junit.Before;
import org.junit.Test;
@@ -241,7 +240,7 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
@@ -265,14 +264,14 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+ .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
@@ -295,14 +294,14 @@
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+ .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
BASE_BYTES + THRESHOLD_BYTES, 22L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
@@ -327,14 +326,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -360,14 +359,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -392,14 +391,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -425,14 +424,14 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -441,7 +440,7 @@
}
private void waitForObserverToIdle() {
- waitForIdleHandler(mObserverHandlerThread, WAIT_TIMEOUT_MS);
- waitForIdleHandler(mHandler, WAIT_TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT_MS);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 956b2a7..a1bb0d5 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -37,10 +37,12 @@
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID;
+import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -50,7 +52,6 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.junit.Assert.assertEquals;
@@ -59,11 +60,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -80,6 +83,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -96,10 +100,13 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkStatsProviderBinder;
import libcore.io.IoUtils;
@@ -116,6 +123,7 @@
import java.time.Clock;
import java.time.ZoneOffset;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Tests for {@link NetworkStatsService}.
@@ -154,11 +162,13 @@
private File mStatsDir;
private @Mock INetworkManagementService mNetManager;
+ private @Mock NetworkStatsFactory mStatsFactory;
private @Mock NetworkStatsSettings mSettings;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
+ @Mock
+ private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
private HandlerThread mHandlerThread;
- private Handler mHandler;
private NetworkStatsService mService;
private INetworkStatsSession mSession;
@@ -187,15 +197,11 @@
PowerManager.WakeLock wakeLock =
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mService = new NetworkStatsService(
- mServiceContext, mNetManager, mAlarmManager, wakeLock, mClock,
- TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
- mStatsDir, getBaseDir(mStatsDir));
mHandlerThread = new HandlerThread("HandlerThread");
- mHandlerThread.start();
- Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
- mHandler = new Handler(mHandlerThread.getLooper(), callback);
- mService.setHandler(mHandler, callback);
+ final NetworkStatsService.Dependencies deps = makeDependencies();
+ mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
+ mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir,
+ getBaseDir(mStatsDir), deps);
mElapsedRealtime = 0L;
@@ -205,18 +211,36 @@
mService.systemReady();
// Verify that system ready fetches realtime stats
- verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ verify(mStatsFactory).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
// catch INetworkManagementEventObserver during systemReady()
ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
- ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+ ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
}
+ @NonNull
+ private NetworkStatsService.Dependencies makeDependencies() {
+ return new NetworkStatsService.Dependencies() {
+ @Override
+ public HandlerThread makeHandlerThread() {
+ return mHandlerThread;
+ }
+
+ @Override
+ public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(
+ @NonNull Context context, @NonNull Executor executor,
+ @NonNull NetworkStatsService service) {
+
+ return mNetworkStatsSubscriptionsMonitor;
+ }
+ };
+ }
+
@After
public void tearDown() throws Exception {
IoUtils.deleteContents(mStatsDir);
@@ -229,6 +253,8 @@
mSession.close();
mService = null;
+
+ mHandlerThread.quitSafely();
}
@Test
@@ -239,9 +265,8 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -251,7 +276,7 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -264,7 +289,7 @@
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
+ .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -283,9 +308,8 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -295,13 +319,13 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+ .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
mService.setUidForeground(UID_RED, false);
mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
mService.setUidForeground(UID_RED, true);
@@ -357,15 +381,14 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS);
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -398,19 +421,18 @@
NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 10);
forcePollAndWaitForIdle();
@@ -428,14 +450,13 @@
expectDefaultSettings();
states = new NetworkState[] {buildMobile3gState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
- expectBandwidthControlCheck();
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -443,12 +464,12 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
+ .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -473,20 +494,20 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -502,12 +523,13 @@
// special "removed" bucket.
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_BLUE);
mServiceContext.sendBroadcast(intent);
@@ -525,23 +547,22 @@
}
@Test
- public void testUid3g4gCombinedByTemplate() throws Exception {
+ public void testUid3gWimaxCombinedByTemplate() throws Exception {
// pretend that network comes online
expectDefaultSettings();
NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 5);
forcePollAndWaitForIdle();
@@ -550,17 +571,16 @@
assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
- // now switch over to 4g network
+ // now switch over to wimax network
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
+ states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- expectBandwidthControlCheck();
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -569,10 +589,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
forcePollAndWaitForIdle();
@@ -582,24 +602,105 @@
}
@Test
+ public void testMobileStatsByRatType() throws Exception {
+ final NetworkTemplate template3g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+ final NetworkTemplate template4g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
+ final NetworkTemplate template5g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
+ final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+
+ // 3G network comes online.
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new VpnInfo[0]);
+
+ // Create some traffic.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 12L, 18L, 14L, 1L, 0L)));
+ forcePollAndWaitForIdle();
+
+ // Verify 3g templates gets stats.
+ assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0);
+ assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+ assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0);
+ assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+ // 4G network comes online.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ // Append more traffic on existing 3g stats entry.
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 16L, 22L, 17L, 2L, 0L))
+ // Add entry that is new on 4g.
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+ 33L, 27L, 8L, 10L, 1L)));
+ forcePollAndWaitForIdle();
+
+ // Verify ALL_MOBILE template gets all. 3g template counters do not increase.
+ assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1);
+ assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+ // Verify 4g template counts appended stats on existing entry and newly created entry.
+ assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+ // Verify 5g template doesn't get anything since no traffic is generated on 5g.
+ assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+ // 5g network comes online.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ // Existing stats remains.
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 16L, 22L, 17L, 2L, 0L))
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+ 33L, 27L, 8L, 10L, 1L))
+ // Add some traffic on 5g.
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 5L, 13L, 31L, 9L, 2L)));
+ forcePollAndWaitForIdle();
+
+ // Verify ALL_MOBILE template gets all.
+ assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3);
+ // 3g/4g template counters do not increase.
+ assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+ assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+ // Verify 5g template gets the 5g count.
+ assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2);
+ }
+
+ // TODO: support per IMSI state
+ private void setMobileRatTypeAndWaitForIdle(int ratType) {
+ when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString()))
+ .thenReturn(ratType);
+ mService.handleOnCollapsedRatTypeChanged();
+ HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
+ }
+
+ @Test
public void testSummaryForAllUid() throws Exception {
// pretend that network comes online
expectDefaultSettings();
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -614,9 +715,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 2048L, 16L, 1024L, 8L, 0L));
forcePollAndWaitForIdle();
// first verify entire history present
@@ -646,9 +748,8 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -661,9 +762,9 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(entry1)
- .addValues(entry2)
- .addValues(entry3));
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -690,9 +791,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -704,28 +804,33 @@
"otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
final String[] ifaceFilter = new String[] { TEST_IFACE };
+ final String[] augmentedIfaceFilter = new String[] { stackedIface, TEST_IFACE };
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
- when(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL), any()))
+ when(mStatsFactory.augmentWithStackedInterfaces(eq(ifaceFilter)))
+ .thenReturn(augmentedIfaceFilter);
+ when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
.thenReturn(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(uidStats));
+ .insertEntry(uidStats));
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
.thenReturn(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(tetheredStats1)
- .addValues(tetheredStats2));
+ .insertEntry(tetheredStats1)
+ .insertEntry(tetheredStats2));
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
- // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
+ // mStatsFactory#readNetworkStatsDetail() has the following invocations:
// 1) NetworkStatsService#systemReady from #setUp.
// 2) mService#forceUpdateIfaces in the test above.
//
// Additionally, we should have one call from the above call to mService#getDetailedUidStats
- // with the augmented ifaceFilter
- verify(mNetManager, times(2)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
- verify(mNetManager, times(1)).getNetworkStatsUidDetail(
- eq(UID_ALL), eq(NetworkStatsFactory.augmentWithStackedInterfaces(ifaceFilter)));
+ // with the augmented ifaceFilter.
+ verify(mStatsFactory, times(2)).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
+ verify(mStatsFactory, times(1)).readNetworkStatsDetail(
+ eq(UID_ALL),
+ eq(augmentedIfaceFilter),
+ eq(TAG_ALL));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size());
@@ -740,17 +845,16 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -764,10 +868,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
mService.setUidForeground(UID_RED, true);
mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
@@ -797,9 +901,8 @@
NetworkState[] states = new NetworkState[] {buildWifiState(true /* isMetered */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -809,9 +912,9 @@
// and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
// We layer them on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
@@ -837,9 +940,8 @@
new NetworkState[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -849,9 +951,9 @@
// ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
// on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
forcePollAndWaitForIdle();
@@ -875,9 +977,8 @@
NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
// create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -885,17 +986,17 @@
// Traffic seen by kernel counters (includes software tethering).
final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+ .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
// Hardware tethering traffic, not seen by kernel counters.
final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+ .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
// Traffic for UID_RED.
final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
// All tethering traffic, both hardware and software.
final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+ .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
0L);
expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
@@ -916,9 +1017,8 @@
NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -936,8 +1036,6 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
-
-
// Register and verify request and that binder was called
DataUsageRequest request =
mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest,
@@ -947,22 +1045,17 @@
long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB
assertEquals(minThresholdInBytes, request.thresholdInBytes);
- // Send dummy message to make sure that any previous message has been handled
- mHandler.sendMessage(mHandler.obtainMessage(-1));
- waitForIdleHandler(mHandler, WAIT_TIMEOUT);
-
-
+ HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
// Make sure that the caller binder gets connected
verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt());
-
// modify some number on wifi, and trigger poll event
// not enough traffic to call data usage callback
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -977,7 +1070,7 @@
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
+ .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -1014,6 +1107,90 @@
mService.unregisterUsageRequest(unknownRequest);
}
+ @Test
+ public void testStatsProviderUpdateStats() throws Exception {
+ // Pretend that network comes online.
+ expectDefaultSettings();
+ final NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST", provider);
+ assertNotNull(cb);
+
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+ // Verifies that one requestStatsUpdate will be called during iface update.
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+
+ // Create some initial traffic and report to the service.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ final NetworkStats expectedStats = new NetworkStats(0L, 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 128L, 2L, 128L, 2L, 1L))
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
+ 64L, 1L, 64L, 1L, 1L));
+ cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+
+ // Make another empty mutable stats object. This is necessary since the new NetworkStats
+ // object will be used to compare with the old one in NetworkStatsRecoder, two of them
+ // cannot be the same object.
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ forcePollAndWaitForIdle();
+
+ // Verifies that one requestStatsUpdate and setAlert will be called during polling.
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ provider.expectOnSetAlert(MB_IN_BYTES);
+
+ // Verifies that service recorded history, does not verify uid tag part.
+ assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
+
+ // Verifies that onStatsUpdated updates the stats accordingly.
+ final NetworkStats stats = mSession.getSummaryForAllUid(
+ sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(2, stats.size());
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L);
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L);
+
+ // Verifies that unregister the callback will remove the provider from service.
+ cb.unregister();
+ forcePollAndWaitForIdle();
+ provider.assertNoCallback();
+ }
+
+ @Test
+ public void testStatsProviderSetAlert() throws Exception {
+ // Pretend that network comes online.
+ expectDefaultSettings();
+ NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */)};
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+
+ // Register custom provider and retrieve callback.
+ final TestableNetworkStatsProviderBinder provider =
+ new TestableNetworkStatsProviderBinder();
+ final INetworkStatsProviderCallback cb =
+ mService.registerNetworkStatsProvider("TEST", provider);
+ assertNotNull(cb);
+
+ // Simulates alert quota of the provider has been reached.
+ cb.notifyAlertReached();
+ HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
+
+ // Verifies that polling is triggered by alert reached.
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ // Verifies that global alert will be re-armed.
+ provider.expectOnSetAlert(MB_IN_BYTES);
+ }
+
private static File getBaseDir(File statsDir) {
File baseDir = new File(statsDir, "netstats");
baseDir.mkdirs();
@@ -1062,7 +1239,6 @@
private void expectSystemReady() throws Exception {
expectNetworkStatsSummary(buildEmptyStats());
- expectBandwidthControlCheck();
}
private String getActiveIface(NetworkState... states) throws Exception {
@@ -1084,11 +1260,11 @@
}
private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception {
- when(mNetManager.getNetworkStatsSummaryDev()).thenReturn(summary);
+ when(mStatsFactory.readNetworkStatsSummaryDev()).thenReturn(summary);
}
private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception {
- when(mNetManager.getNetworkStatsSummaryXt()).thenReturn(summary);
+ when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary);
}
private void expectNetworkStatsTethering(int how, NetworkStats stats)
@@ -1102,7 +1278,8 @@
private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
throws Exception {
- when(mNetManager.getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL)).thenReturn(detail);
+ when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL))
+ .thenReturn(detail);
// also include tethering details, since they are folded into UID
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
@@ -1115,7 +1292,9 @@
private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
throws Exception {
when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
+ when(mSettings.getPollDelay()).thenReturn(0L);
when(mSettings.getSampleEnabled()).thenReturn(true);
+ when(mSettings.getCombineSubtypeEnabled()).thenReturn(false);
final Config config = new Config(bucketDuration, deleteAge, deleteAge);
when(mSettings.getDevConfig()).thenReturn(config);
@@ -1130,10 +1309,6 @@
when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES);
}
- private void expectBandwidthControlCheck() throws Exception {
- when(mNetManager.isBandwidthControlEnabled()).thenReturn(true);
- }
-
private void assertStatsFilesExist(boolean exist) {
final File basePath = new File(mStatsDir, "netstats");
if (exist) {
@@ -1165,6 +1340,7 @@
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
+ capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
@@ -1182,10 +1358,11 @@
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
+ capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
- private static NetworkState buildMobile4gState(String iface) {
+ private static NetworkState buildWimaxState(@NonNull String iface) {
final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
@@ -1226,9 +1403,7 @@
private void forcePollAndWaitForIdle() {
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
- // Send dummy message to make sure that any previous message has been handled
- mHandler.sendMessage(mHandler.obtainMessage(-1));
- waitForIdleHandler(mHandler, WAIT_TIMEOUT);
+ HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
}
static class LatchedHandler extends Handler {
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
new file mode 100644
index 0000000..c91dfec
--- /dev/null
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.os.Looper;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.net.NetworkStatsSubscriptionsMonitor.Delegate;
+import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@RunWith(JUnit4.class)
+public final class NetworkStatsSubscriptionsMonitorTest {
+ private static final int TEST_SUBID1 = 3;
+ private static final int TEST_SUBID2 = 5;
+ private static final String TEST_IMSI1 = "466921234567890";
+ private static final String TEST_IMSI2 = "466920987654321";
+ private static final String TEST_IMSI3 = "466929999999999";
+
+ @Mock private Context mContext;
+ @Mock private SubscriptionManager mSubscriptionManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private Delegate mDelegate;
+ private final List<Integer> mTestSubList = new ArrayList<>();
+
+ private final Executor mExecutor = Executors.newSingleThreadExecutor();
+ private NetworkStatsSubscriptionsMonitor mMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+ when(mContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+ .thenReturn(mSubscriptionManager);
+ when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE)))
+ .thenReturn(mTelephonyManager);
+
+ mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mExecutor, mDelegate);
+ }
+
+ @Test
+ public void testStartStop() {
+ // Verify that addOnSubscriptionsChangedListener() is never called before start().
+ verify(mSubscriptionManager, never())
+ .addOnSubscriptionsChangedListener(mExecutor, mMonitor);
+ mMonitor.start();
+ verify(mSubscriptionManager).addOnSubscriptionsChangedListener(mExecutor, mMonitor);
+
+ // Verify that removeOnSubscriptionsChangedListener() is never called before stop()
+ verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(mMonitor);
+ mMonitor.stop();
+ verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mMonitor);
+ }
+
+ @NonNull
+ private static int[] convertArrayListToIntArray(@NonNull List<Integer> arrayList) {
+ final int[] list = new int[arrayList.size()];
+ for (int i = 0; i < arrayList.size(); i++) {
+ list[i] = arrayList.get(i);
+ }
+ return list;
+ }
+
+ private void setRatTypeForSub(List<RatTypeListener> listeners,
+ int subId, int type) {
+ final ServiceState serviceState = mock(ServiceState.class);
+ when(serviceState.getDataNetworkType()).thenReturn(type);
+ final RatTypeListener match = CollectionUtils
+ .find(listeners, it -> it.getSubId() == subId);
+ if (match != null) {
+ match.onServiceStateChanged(serviceState);
+ }
+ }
+
+ private void addTestSub(int subId, String subscriberId) {
+ // add SubId to TestSubList.
+ if (!mTestSubList.contains(subId)) {
+ mTestSubList.add(subId);
+ }
+ final int[] subList = convertArrayListToIntArray(mTestSubList);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
+ when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
+ mMonitor.onSubscriptionsChanged();
+ }
+
+ private void removeTestSub(int subId) {
+ // Remove subId from TestSubList.
+ mTestSubList.removeIf(it -> it == subId);
+ final int[] subList = convertArrayListToIntArray(mTestSubList);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList);
+ mMonitor.onSubscriptionsChanged();
+ }
+
+ private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
+ assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+ final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
+ // Verify callback with the subscriberId and the RAT type should be as expected.
+ // It will fail if get a callback with an unexpected RAT type.
+ verify(mDelegate).onCollapsedRatTypeChanged(eq(subscriberId), typeCaptor.capture());
+ final int type = typeCaptor.getValue();
+ assertEquals(ratType, type);
+ }
+
+ private void assertRatTypeNotChangedForSub(String subscriberId, int ratType) {
+ assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+ // Should never get callback with any RAT type.
+ verify(mDelegate, never()).onCollapsedRatTypeChanged(eq(subscriberId), anyInt());
+ }
+
+ @Test
+ public void testSubChangedAndRatTypeChanged() {
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ // Insert sim2.
+ addTestSub(TEST_SUBID2, TEST_IMSI2);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ verify(mTelephonyManager, times(2)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to UMTS.
+ // Verify RAT type of sim1 after subscription gets onCollapsedRatTypeChanged() callback
+ // and others remain untouched.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Set RAT type of sim2 to LTE.
+ // Verify RAT type of sim2 after subscription gets onCollapsedRatTypeChanged() callback
+ // and others remain untouched.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID2,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_LTE);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Remove sim2 and verify that callbacks are fired and RAT type is correct for sim2.
+ // while the other two remain untouched.
+ removeTestSub(TEST_SUBID2);
+ verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to UNKNOWN. Then stop monitoring subscription changes
+ // and verify that the listener for sim1 is removed.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ mMonitor.stop();
+ verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+
+
+ @Test
+ public void test5g() {
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type. Also capture listener for later use.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ final RatTypeListener listener = CollectionUtils
+ .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1);
+ assertNotNull(listener);
+
+ // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs
+ // NETWORK_TYPE_5G_NSA.
+ final ServiceState serviceState = mock(ServiceState.class);
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA);
+ reset(mDelegate);
+
+ // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+ reset(mDelegate);
+
+ // Verify NR connected with other RAT type does not take effect.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+
+ // Set RAT type to 5G standalone mode, the RAT type should be NR.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_NR);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ reset(mDelegate);
+
+ // Set NR state to none in standalone mode does not change anything.
+ when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR);
+ when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+ listener.onServiceStateChanged(serviceState);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+ }
+}
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
new file mode 100644
index 0000000..eb0513b
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
@@ -0,0 +1,9 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 test_nss_tun1 0x0 1001 0 3000 300 700 70 0 0 0 0 0 0 0 0 0 0 0 0
+5 test_nss_tun1 0x0 1002 0 500 50 250 25 0 0 0 0 0 0 0 0 0 0 0 0
+6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+7 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
+8 test1 0x0 1004 0 3850 350 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 6cd7499..f04b32f 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
+10 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,5 +41,3 @@
41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
-45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index 9f86153..12d98ca 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index b37fae6..a1d6d41 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,4 @@
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
-5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
+4 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp
index ef1ad2c..84ae2b5 100644
--- a/tests/net/smoketest/Android.bp
+++ b/tests/net/smoketest/Android.bp
@@ -14,4 +14,9 @@
defaults: ["FrameworksNetTests-jni-defaults"],
srcs: ["java/SmokeTest.java"],
test_suites: ["device-tests"],
-}
+ static_libs: [
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "services.core",
+ ],
+}
\ No newline at end of file