Move module sources to packages/Connectivity
Files that are planned to be part of the connectivity module are grouped
in packages/Connectivity, so they can be built separately and moved in
one operation with their history into packages/modules/Connectivity.
This places the files in the existing framework-connectivity-sources
filegroup instead of the current framework-core-sources filegroup. Both
are used the same way in framework-non-updatable-sources.
Bug: 171540887
Test: m
Change-Id: I62d9d91574ace6f5c4624035d190260c3126b91e
diff --git a/framework/src/android/net/CaptivePortal.java b/framework/src/android/net/CaptivePortal.java
new file mode 100644
index 0000000..269bbf2
--- /dev/null
+++ b/framework/src/android/net/CaptivePortal.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed urnder 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.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * 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
+ * passed as an extra named {@link ConnectivityManager#EXTRA_CAPTIVE_PORTAL} with the
+ * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} activity.
+ */
+public class CaptivePortal implements Parcelable {
+ /**
+ * Response code from the captive portal application, indicating that the portal was dismissed
+ * and the network should be re-validated.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
+ @SystemApi
+ public static final int APP_RETURN_DISMISSED = 0;
+ /**
+ * Response code from the captive portal application, indicating that the user did not login and
+ * does not want to use the captive portal network.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
+ @SystemApi
+ public static final int APP_RETURN_UNWANTED = 1;
+ /**
+ * Response code from the captive portal application, indicating that the user does not wish to
+ * login but wants to use the captive portal network as-is.
+ * @see ICaptivePortal#appResponse(int)
+ * @see android.net.INetworkMonitor#notifyCaptivePortalAppFinished(int)
+ * @hide
+ */
+ @SystemApi
+ 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
+ public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0;
+
+ private final IBinder mBinder;
+
+ /** @hide */
+ public CaptivePortal(@NonNull IBinder binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStrongBinder(mBinder);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<CaptivePortal> CREATOR
+ = new Parcelable.Creator<CaptivePortal>() {
+ @Override
+ public CaptivePortal createFromParcel(Parcel in) {
+ return new CaptivePortal(in.readStrongBinder());
+ }
+
+ @Override
+ public CaptivePortal[] newArray(int size) {
+ return new CaptivePortal[size];
+ }
+ };
+
+ /**
+ * Indicate to the system that the captive portal has been
+ * dismissed. In response the framework will re-evaluate the network's
+ * connectivity and might take further action thereafter.
+ */
+ public void reportCaptivePortalDismissed() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_DISMISSED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicate to the system that the user does not want to pursue signing in to the
+ * captive portal and the system should continue to prefer other networks
+ * without captive portals for use as the default active data network. The
+ * system will not retest the network for a captive portal so as to avoid
+ * disturbing the user with further sign in to network notifications.
+ */
+ public void ignoreNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_UNWANTED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicate to the system the user wants to use this network as is, even though
+ * the captive portal is still in place. The system will treat the network
+ * as if it did not have a captive portal when selecting the network to use
+ * as the default active data network. This may result in this network
+ * becoming the default active data network, which could disrupt network
+ * connectivity for apps because the captive portal is still in place.
+ * @hide
+ */
+ @SystemApi
+ public void useNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Request that the system reevaluates the captive portal status.
+ * @hide
+ */
+ @SystemApi
+ @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.
+ * @hide
+ */
+ @SystemApi
+ public void logEvent(int eventId, @NonNull String packageName) {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/framework/src/android/net/CaptivePortalData.aidl b/framework/src/android/net/CaptivePortalData.aidl
new file mode 100644
index 0000000..1d57ee7
--- /dev/null
+++ b/framework/src/android/net/CaptivePortalData.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+@JavaOnlyStableParcelable parcelable CaptivePortalData;
diff --git a/framework/src/android/net/CaptivePortalData.java b/framework/src/android/net/CaptivePortalData.java
new file mode 100644
index 0000000..18467fa
--- /dev/null
+++ b/framework/src/android/net/CaptivePortalData.java
@@ -0,0 +1,305 @@
+/*
+ * 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.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
+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 final String mVenueFriendlyName;
+
+ private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
+ boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
+ String venueFriendlyName) {
+ mRefreshTimeMillis = refreshTimeMillis;
+ mUserPortalUrl = userPortalUrl;
+ mVenueInfoUrl = venueInfoUrl;
+ mIsSessionExtendable = isSessionExtendable;
+ mByteLimit = byteLimit;
+ mExpiryTimeMillis = expiryTimeMillis;
+ mCaptive = captive;
+ mVenueFriendlyName = venueFriendlyName;
+ }
+
+ private CaptivePortalData(Parcel p) {
+ this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
+ p.readLong(), p.readLong(), p.readBoolean(), p.readString());
+ }
+
+ @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);
+ dest.writeString(mVenueFriendlyName);
+ }
+
+ /**
+ * 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;
+ private String mVenueFriendlyName;
+
+ /**
+ * 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)
+ .setVenueFriendlyName(data.mVenueFriendlyName);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Set the venue friendly name.
+ */
+ @NonNull
+ public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
+ mVenueFriendlyName = venueFriendlyName;
+ return this;
+ }
+
+ /**
+ * Create a new {@link CaptivePortalData}.
+ */
+ @NonNull
+ public CaptivePortalData build() {
+ return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
+ mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
+ mVenueFriendlyName);
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Get the venue friendly name
+ */
+ @Nullable
+ public String getVenueFriendlyName() {
+ return mVenueFriendlyName;
+ }
+
+ @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, mVenueFriendlyName);
+ }
+
+ @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
+ && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
+ }
+
+ @Override
+ public String toString() {
+ return "CaptivePortalData {"
+ + "refreshTime: " + mRefreshTimeMillis
+ + ", userPortalUrl: " + mUserPortalUrl
+ + ", venueInfoUrl: " + mVenueInfoUrl
+ + ", isSessionExtendable: " + mIsSessionExtendable
+ + ", byteLimit: " + mByteLimit
+ + ", expiryTime: " + mExpiryTimeMillis
+ + ", captive: " + mCaptive
+ + ", venueFriendlyName: " + mVenueFriendlyName
+ + "}";
+ }
+}
diff --git a/framework/src/android/net/ConnectionInfo.aidl b/framework/src/android/net/ConnectionInfo.aidl
new file mode 100644
index 0000000..07faf8b
--- /dev/null
+++ b/framework/src/android/net/ConnectionInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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;
+
+parcelable ConnectionInfo;
diff --git a/framework/src/android/net/ConnectionInfo.java b/framework/src/android/net/ConnectionInfo.java
new file mode 100644
index 0000000..4514a84
--- /dev/null
+++ b/framework/src/android/net/ConnectionInfo.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Describe a network connection including local and remote address/port of a connection and the
+ * transport protocol.
+ *
+ * @hide
+ */
+public final class ConnectionInfo implements Parcelable {
+ public final int protocol;
+ public final InetSocketAddress local;
+ public final InetSocketAddress remote;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public ConnectionInfo(int protocol, InetSocketAddress local, InetSocketAddress remote) {
+ this.protocol = protocol;
+ this.local = local;
+ this.remote = remote;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(protocol);
+ out.writeByteArray(local.getAddress().getAddress());
+ out.writeInt(local.getPort());
+ out.writeByteArray(remote.getAddress().getAddress());
+ out.writeInt(remote.getPort());
+ }
+
+ public static final @android.annotation.NonNull Creator<ConnectionInfo> CREATOR = new Creator<ConnectionInfo>() {
+ public ConnectionInfo createFromParcel(Parcel in) {
+ int protocol = in.readInt();
+ InetAddress localAddress;
+ try {
+ localAddress = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("Invalid InetAddress");
+ }
+ int localPort = in.readInt();
+ InetAddress remoteAddress;
+ try {
+ remoteAddress = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("Invalid InetAddress");
+ }
+ int remotePort = in.readInt();
+ InetSocketAddress local = new InetSocketAddress(localAddress, localPort);
+ InetSocketAddress remote = new InetSocketAddress(remoteAddress, remotePort);
+ return new ConnectionInfo(protocol, local, remote);
+ }
+
+ public ConnectionInfo[] newArray(int size) {
+ return new ConnectionInfo[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/ConnectivityDiagnosticsManager.aidl b/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
new file mode 100644
index 0000000..82ba0ca
--- /dev/null
+++ b/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
@@ -0,0 +1,21 @@
+/**
+ *
+ * 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;
+
+parcelable ConnectivityDiagnosticsManager.ConnectivityReport;
+parcelable ConnectivityDiagnosticsManager.DataStallReport;
\ No newline at end of file
diff --git a/framework/src/android/net/ConnectivityDiagnosticsManager.java b/framework/src/android/net/ConnectivityDiagnosticsManager.java
new file mode 100644
index 0000000..5234494
--- /dev/null
+++ b/framework/src/android/net/ConnectivityDiagnosticsManager.java
@@ -0,0 +1,777 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * Class that provides utilities for collecting network connectivity diagnostics information.
+ * Connectivity information is made available through triggerable diagnostics tools and by listening
+ * to System validations. Some diagnostics information may be permissions-restricted.
+ *
+ * <p>ConnectivityDiagnosticsManager is intended for use by applications offering network
+ * connectivity on a user device. These tools will provide several mechanisms for these applications
+ * to be alerted to network conditions as well as diagnose potential network issues themselves.
+ *
+ * <p>The primary responsibilities of this class are to:
+ *
+ * <ul>
+ * <li>Allow permissioned applications to register and unregister callbacks for network event
+ * notifications
+ * <li>Invoke callbacks for network event notifications, including:
+ * <ul>
+ * <li>Network validations
+ * <li>Data stalls
+ * <li>Connectivity reports from applications
+ * </ul>
+ * </ul>
+ */
+public class ConnectivityDiagnosticsManager {
+ /** @hide */
+ @VisibleForTesting
+ public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
+ sCallbacks = new ConcurrentHashMap<>();
+
+ private final Context mContext;
+ private final IConnectivityManager mService;
+
+ /** @hide */
+ public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static boolean persistableBundleEquals(
+ @Nullable PersistableBundle a, @Nullable PersistableBundle b) {
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ if (!Objects.equals(a.keySet(), b.keySet())) return false;
+ for (String key : a.keySet()) {
+ if (!Objects.equals(a.get(key), b.get(key))) return false;
+ }
+ return true;
+ }
+
+ /** Class that includes connectivity information for a specific Network at a specific time. */
+ public static final class ConnectivityReport implements Parcelable {
+ /**
+ * The overall status of the network is that it is invalid; it neither provides
+ * connectivity nor has been exempted from validation.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
+
+ /**
+ * The overall status of the network is that it is valid, this may be because it provides
+ * full Internet access (all probes succeeded), or because other properties of the network
+ * caused probes not to be run.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID
+ public static final int NETWORK_VALIDATION_RESULT_VALID = 1;
+
+ /**
+ * The overall status of the network is that it provides partial connectivity; some
+ * probed services succeeded but others failed.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
+ public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2;
+
+ /**
+ * Due to the properties of the network, validation was not performed.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_VALIDATION_RESULT_"},
+ value = {
+ NETWORK_VALIDATION_RESULT_INVALID,
+ NETWORK_VALIDATION_RESULT_VALID,
+ NETWORK_VALIDATION_RESULT_PARTIALLY_VALID,
+ NETWORK_VALIDATION_RESULT_SKIPPED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkValidationResult {}
+
+ /**
+ * The overall validation result for the Network being reported on.
+ *
+ * <p>The possible values for this key are:
+ * {@link #NETWORK_VALIDATION_RESULT_INVALID},
+ * {@link #NETWORK_VALIDATION_RESULT_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
+ *
+ * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
+ */
+ @NetworkValidationResult
+ public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+
+ /** DNS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS
+ public static final int NETWORK_PROBE_DNS = 0x04;
+
+ /** HTTP probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP
+ public static final int NETWORK_PROBE_HTTP = 0x08;
+
+ /** HTTPS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+ public static final int NETWORK_PROBE_HTTPS = 0x10;
+
+ /** Captive portal fallback probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK
+ public static final int NETWORK_PROBE_FALLBACK = 0x20;
+
+ /** Private DNS (DNS over TLS) probd. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS
+ public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_PROBE_"},
+ value = {
+ NETWORK_PROBE_DNS,
+ NETWORK_PROBE_HTTP,
+ NETWORK_PROBE_HTTPS,
+ NETWORK_PROBE_FALLBACK,
+ NETWORK_PROBE_PRIVATE_DNS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkProbe {}
+
+ /**
+ * A bitmask of network validation probes that succeeded.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK =
+ "networkProbesSucceeded";
+
+ /**
+ * A bitmask of network validation probes that were attempted.
+ *
+ * <p>These probes may have failed or may be incomplete at the time of this report.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
+ "networkProbesAttempted";
+
+ /** @hide */
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK,
+ KEY_NETWORK_PROBES_ATTEMPTED_BITMASK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConnectivityReportBundleKeys {}
+
+ /** The Network for which this ConnectivityReport applied */
+ @NonNull private final Network mNetwork;
+
+ /**
+ * The timestamp for the report. The timestamp is taken from {@link
+ * System#currentTimeMillis}.
+ */
+ private final long mReportTimestamp;
+
+ /** LinkProperties available on the Network at the reported timestamp */
+ @NonNull private final LinkProperties mLinkProperties;
+
+ /** NetworkCapabilities available on the Network at the reported timestamp */
+ @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
+ /** PersistableBundle that may contain additional info about the report */
+ @NonNull private final PersistableBundle mAdditionalInfo;
+
+ /**
+ * Constructor for ConnectivityReport.
+ *
+ * <p>Apps should obtain instances through {@link
+ * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
+ * their own instances (unless for testing purposes).
+ *
+ * @param network The Network for which this ConnectivityReport applies
+ * @param reportTimestamp The timestamp for the report
+ * @param linkProperties The LinkProperties available on network at reportTimestamp
+ * @param networkCapabilities The NetworkCapabilities available on network at
+ * reportTimestamp
+ * @param additionalInfo A PersistableBundle that may contain additional info about the
+ * report
+ */
+ public ConnectivityReport(
+ @NonNull Network network,
+ long reportTimestamp,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull PersistableBundle additionalInfo) {
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mLinkProperties = new LinkProperties(linkProperties);
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
+ mAdditionalInfo = additionalInfo;
+ }
+
+ /**
+ * Returns the Network for this ConnectivityReport.
+ *
+ * @return The Network for which this ConnectivityReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the LinkProperties available when this report was taken.
+ *
+ * @return LinkProperties available on the Network at the reported timestamp
+ */
+ @NonNull
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ /**
+ * Returns the NetworkCapabilities when this report was taken.
+ *
+ * @return NetworkCapabilities available on the Network at the reported timestamp
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * @return PersistableBundle that may contain additional info about the report
+ */
+ @NonNull
+ public PersistableBundle getAdditionalInfo() {
+ return new PersistableBundle(mAdditionalInfo);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConnectivityReport)) return false;
+ final ConnectivityReport that = (ConnectivityReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mNetwork.equals(that.mNetwork)
+ && mLinkProperties.equals(that.mLinkProperties)
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities)
+ && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mNetwork,
+ mReportTimestamp,
+ mLinkProperties,
+ mNetworkCapabilities,
+ mAdditionalInfo);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeParcelable(mLinkProperties, flags);
+ dest.writeParcelable(mNetworkCapabilities, flags);
+ dest.writeParcelable(mAdditionalInfo, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<ConnectivityReport> CREATOR =
+ new Creator<ConnectivityReport>() {
+ public ConnectivityReport createFromParcel(Parcel in) {
+ return new ConnectivityReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readParcelable(null),
+ in.readParcelable(null),
+ in.readParcelable(null));
+ }
+
+ public ConnectivityReport[] newArray(int size) {
+ return new ConnectivityReport[size];
+ }
+ };
+ }
+
+ /** Class that includes information for a suspected data stall on a specific Network */
+ public static final class DataStallReport implements Parcelable {
+ /**
+ * Indicates that the Data Stall was detected using DNS events.
+ */
+ public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+
+ /**
+ * Indicates that the Data Stall was detected using TCP metrics.
+ */
+ public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"DETECTION_METHOD_"},
+ value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+ public @interface DetectionMethod {}
+
+ /**
+ * This key represents the period in milliseconds over which other included TCP metrics
+ * were measured.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS =
+ "tcpMetricsCollectionPeriodMillis";
+
+ /**
+ * This key represents the fail rate of TCP packets when the suspected data stall was
+ * detected.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int percentage between 0 and 100.
+ */
+ public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+
+ /**
+ * This key represents the consecutive number of DNS timeouts that have occurred.
+ *
+ * <p>The consecutive count will be reset any time a DNS response is received.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_DNS_EVENTS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_TCP_PACKET_FAIL_RATE,
+ KEY_DNS_CONSECUTIVE_TIMEOUTS
+ })
+ public @interface DataStallReportBundleKeys {}
+
+ /** The Network for which this DataStallReport applied */
+ @NonNull private final Network mNetwork;
+
+ /**
+ * The timestamp for the report. The timestamp is taken from {@link
+ * System#currentTimeMillis}.
+ */
+ private long mReportTimestamp;
+
+ /** A bitmask of the detection methods used to identify the suspected data stall */
+ @DetectionMethod private final int mDetectionMethod;
+
+ /** LinkProperties available on the Network at the reported timestamp */
+ @NonNull private final LinkProperties mLinkProperties;
+
+ /** NetworkCapabilities available on the Network at the reported timestamp */
+ @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
+ /** PersistableBundle that may contain additional information on the suspected data stall */
+ @NonNull private final PersistableBundle mStallDetails;
+
+ /**
+ * Constructor for DataStallReport.
+ *
+ * <p>Apps should obtain instances through {@link
+ * ConnectivityDiagnosticsCallback#onDataStallSuspected} instead of instantiating their own
+ * instances (unless for testing purposes).
+ *
+ * @param network The Network for which this DataStallReport applies
+ * @param reportTimestamp The timestamp for the report
+ * @param detectionMethod The detection method used to identify this data stall
+ * @param linkProperties The LinkProperties available on network at reportTimestamp
+ * @param networkCapabilities The NetworkCapabilities available on network at
+ * reportTimestamp
+ * @param stallDetails A PersistableBundle that may contain additional info about the report
+ */
+ public DataStallReport(
+ @NonNull Network network,
+ long reportTimestamp,
+ @DetectionMethod int detectionMethod,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull PersistableBundle stallDetails) {
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mDetectionMethod = detectionMethod;
+ mLinkProperties = new LinkProperties(linkProperties);
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
+ mStallDetails = stallDetails;
+ }
+
+ /**
+ * Returns the Network for this DataStallReport.
+ *
+ * @return The Network for which this DataStallReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the bitmask of detection methods used to identify this suspected data stall.
+ *
+ * @return The bitmask of detection methods used to identify the suspected data stall
+ */
+ public int getDetectionMethod() {
+ return mDetectionMethod;
+ }
+
+ /**
+ * Returns the LinkProperties available when this report was taken.
+ *
+ * @return LinkProperties available on the Network at the reported timestamp
+ */
+ @NonNull
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ /**
+ * Returns the NetworkCapabilities when this report was taken.
+ *
+ * @return NetworkCapabilities available on the Network at the reported timestamp
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * <p>Gets a bundle with details about the suspected data stall including information
+ * specific to the monitoring method that detected the data stall.
+ *
+ * @return PersistableBundle that may contain additional information on the suspected data
+ * stall
+ */
+ @NonNull
+ public PersistableBundle getStallDetails() {
+ return new PersistableBundle(mStallDetails);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DataStallReport)) return false;
+ final DataStallReport that = (DataStallReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mDetectionMethod == that.mDetectionMethod
+ && mNetwork.equals(that.mNetwork)
+ && mLinkProperties.equals(that.mLinkProperties)
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities)
+ && persistableBundleEquals(mStallDetails, that.mStallDetails);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mNetwork,
+ mReportTimestamp,
+ mDetectionMethod,
+ mLinkProperties,
+ mNetworkCapabilities,
+ mStallDetails);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeInt(mDetectionMethod);
+ dest.writeParcelable(mLinkProperties, flags);
+ dest.writeParcelable(mNetworkCapabilities, flags);
+ dest.writeParcelable(mStallDetails, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<DataStallReport> CREATOR =
+ new Creator<DataStallReport>() {
+ public DataStallReport createFromParcel(Parcel in) {
+ return new DataStallReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readInt(),
+ in.readParcelable(null),
+ in.readParcelable(null),
+ in.readParcelable(null));
+ }
+
+ public DataStallReport[] newArray(int size) {
+ return new DataStallReport[size];
+ }
+ };
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static class ConnectivityDiagnosticsBinder
+ extends IConnectivityDiagnosticsCallback.Stub {
+ @NonNull private final ConnectivityDiagnosticsCallback mCb;
+ @NonNull private final Executor mExecutor;
+
+ /** @hide */
+ @VisibleForTesting
+ public ConnectivityDiagnosticsBinder(
+ @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) {
+ this.mCb = cb;
+ this.mExecutor = executor;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mCb.onConnectivityReportAvailable(report);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onDataStallSuspected(@NonNull DataStallReport report) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mCb.onDataStallSuspected(report);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void onNetworkConnectivityReported(
+ @NonNull Network network, boolean hasConnectivity) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mCb.onNetworkConnectivityReported(network, hasConnectivity);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ /**
+ * Abstract base class for Connectivity Diagnostics callbacks. Used for notifications about
+ * network connectivity events. Must be extended by applications wanting notifications.
+ */
+ public abstract static class ConnectivityDiagnosticsCallback {
+ /**
+ * Called when the platform completes a data connectivity check. This will also be invoked
+ * immediately upon registration for each network matching the request with the latest
+ * report, if a report has already been generated for that network.
+ *
+ * <p>The Network specified in the ConnectivityReport may not be active any more when this
+ * method is invoked.
+ *
+ * @param report The ConnectivityReport containing information about a connectivity check
+ */
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
+
+ /**
+ * Called when the platform suspects a data stall on some Network.
+ *
+ * <p>The Network specified in the DataStallReport may not be active any more when this
+ * method is invoked.
+ *
+ * @param report The DataStallReport containing information about the suspected data stall
+ */
+ public void onDataStallSuspected(@NonNull DataStallReport report) {}
+
+ /**
+ * Called when any app reports connectivity to the System.
+ *
+ * @param network The Network for which connectivity has been reported
+ * @param hasConnectivity The connectivity reported to the System
+ */
+ public void onNetworkConnectivityReported(
+ @NonNull Network network, boolean hasConnectivity) {}
+ }
+
+ /**
+ * Registers a ConnectivityDiagnosticsCallback with the System.
+ *
+ * <p>Only apps that offer network connectivity to the user should be registering callbacks.
+ * These are the only apps whose callbacks will be invoked by the system. Apps considered to
+ * meet these conditions include:
+ *
+ * <ul>
+ * <li>Carrier apps with active subscriptions
+ * <li>Active VPNs
+ * <li>WiFi Suggesters
+ * </ul>
+ *
+ * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
+ *
+ * <p>If a registering app loses its relevant permissions, any callbacks it registered will
+ * silently stop receiving callbacks.
+ *
+ * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
+ * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
+ * multiple NetworkRequests, an IllegalArgumentException will be thrown.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * callbacks in {@link ConnectivityManager}. Registering a callback with this method will count
+ * toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting
+ * this issue and to conserve resources, make sure to unregister the callbacks with
+ * {@link #unregisterConnectivityDiagnosticsCallback}.
+ *
+ * @param request The NetworkRequest that will be used to match with Networks for which
+ * callbacks will be fired
+ * @param e The Executor to be used for running the callback method invocations
+ * @param callback The ConnectivityDiagnosticsCallback that the caller wants registered with the
+ * System
+ * @throws IllegalArgumentException if the same callback instance is registered with multiple
+ * NetworkRequests
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ */
+ public void registerConnectivityDiagnosticsCallback(
+ @NonNull NetworkRequest request,
+ @NonNull Executor e,
+ @NonNull ConnectivityDiagnosticsCallback callback) {
+ final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
+ if (sCallbacks.putIfAbsent(callback, binder) != null) {
+ throw new IllegalArgumentException("Callback is currently registered");
+ }
+
+ try {
+ mService.registerConnectivityDiagnosticsCallback(
+ binder, request, mContext.getOpPackageName());
+ } catch (RemoteException exception) {
+ exception.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a ConnectivityDiagnosticsCallback with the System.
+ *
+ * <p>If the given callback is not currently registered with the System, this operation will be
+ * a no-op.
+ *
+ * @param callback The ConnectivityDiagnosticsCallback to be unregistered from the System.
+ */
+ public void unregisterConnectivityDiagnosticsCallback(
+ @NonNull ConnectivityDiagnosticsCallback callback) {
+ // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
+ // atomic.
+ final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
+ if (binder == null) return;
+
+ try {
+ mService.unregisterConnectivityDiagnosticsCallback(binder);
+ } catch (RemoteException exception) {
+ exception.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
new file mode 100644
index 0000000..7f07bba
--- /dev/null
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -0,0 +1,5058 @@
+/*
+ * Copyright (C) 2008 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.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
+import static android.net.NetworkRequest.Type.LISTEN;
+import static android.net.NetworkRequest.Type.REQUEST;
+import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
+import static android.net.QosCallback.QosCallbackRegistrationException;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+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;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkActivityListener;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+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;
+import android.os.ServiceManager;
+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.Range;
+import android.util.SparseIntArray;
+
+import com.android.connectivity.aidl.INetworkAgent;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.Protocol;
+
+import libcore.net.event.NetworkEventDispatcher;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collection;
+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;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * Class that answers queries about the state of network connectivity. It also
+ * notifies applications when network connectivity changes.
+ * <p>
+ * The primary responsibilities of this class are to:
+ * <ol>
+ * <li>Monitor network connections (Wi-Fi, GPRS, UMTS, etc.)</li>
+ * <li>Send broadcast intents when network connectivity changes</li>
+ * <li>Attempt to "fail over" to another network when connectivity to a network
+ * is lost</li>
+ * <li>Provide an API that allows applications to query the coarse-grained or fine-grained
+ * state of the available networks</li>
+ * <li>Provide an API that allows applications to request and select networks for their data
+ * traffic</li>
+ * </ol>
+ */
+@SystemService(Context.CONNECTIVITY_SERVICE)
+public class ConnectivityManager {
+ private static final String TAG = "ConnectivityManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * A change in network connectivity has occurred. A default connection has either
+ * been established or lost. The NetworkInfo for the affected network is
+ * sent as an extra; it should be consulted to see what kind of
+ * connectivity event occurred.
+ * <p/>
+ * Apps targeting Android 7.0 (API level 24) and higher do not receive this
+ * broadcast if they declare the broadcast receiver in their manifest. Apps
+ * will still receive broadcasts if they register their
+ * {@link android.content.BroadcastReceiver} with
+ * {@link android.content.Context#registerReceiver Context.registerReceiver()}
+ * and that context is still valid.
+ * <p/>
+ * If this is a connection that was the result of failing over from a
+ * disconnected network, then the FAILOVER_CONNECTION boolean extra is
+ * set to true.
+ * <p/>
+ * For a loss of connectivity, if the connectivity manager is attempting
+ * to connect (or has already connected) to another network, the
+ * NetworkInfo for the new network is also passed as an extra. This lets
+ * any receivers of the broadcast know that they should not necessarily
+ * tell the user that no data traffic will be possible. Instead, the
+ * receiver should expect another broadcast soon, indicating either that
+ * the failover attempt succeeded (and so there is still overall data
+ * connectivity), or that the failover attempt failed, meaning that all
+ * connectivity has been lost.
+ * <p/>
+ * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
+ * is set to {@code true} if there are no connected networks at all.
+ *
+ * @deprecated apps should use the more versatile {@link #requestNetwork},
+ * {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+ * functions instead for faster and more detailed updates about the network
+ * changes they care about.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
+ public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+
+ /**
+ * 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,
+ * and the user invoked the notification's action indicating they
+ * desire to sign in to the network. Apps handling this activity should
+ * facilitate signing in to the network. This action includes a
+ * {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents
+ * the network presenting the captive portal; all communication with the
+ * captive portal must be done using this {@code Network} object.
+ * <p/>
+ * This activity includes a {@link CaptivePortal} extra named
+ * {@link #EXTRA_CAPTIVE_PORTAL} that can be used to indicate different
+ * outcomes of the captive portal sign in to the system:
+ * <ul>
+ * <li> When the app handling this action believes the user has signed in to
+ * the network and the captive portal has been dismissed, the app should
+ * call {@link CaptivePortal#reportCaptivePortalDismissed} so the system can
+ * reevaluate the network. If reevaluation finds the network no longer
+ * subject to a captive portal, the network may become the default active
+ * data network.</li>
+ * <li> When the app handling this action believes the user explicitly wants
+ * to ignore the captive portal and the network, the app should call
+ * {@link CaptivePortal#ignoreNetwork}. </li>
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+
+ /**
+ * The lookup key for a {@link NetworkInfo} object. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ *
+ * @deprecated The {@link NetworkInfo} object is deprecated, as many of its properties
+ * can't accurately represent modern network characteristics.
+ * Please obtain information about networks from the {@link NetworkCapabilities}
+ * or {@link LinkProperties} objects instead.
+ */
+ @Deprecated
+ public static final String EXTRA_NETWORK_INFO = "networkInfo";
+
+ /**
+ * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
+ *
+ * @see android.content.Intent#getIntExtra(String, int)
+ * @deprecated The network type is not rich enough to represent the characteristics
+ * of modern networks. Please use {@link NetworkCapabilities} instead,
+ * in particular the transports.
+ */
+ @Deprecated
+ public static final String EXTRA_NETWORK_TYPE = "networkType";
+
+ /**
+ * The lookup key for a boolean that indicates whether a connect event
+ * is for a network to which the connectivity manager was failing over
+ * following a disconnect on another network.
+ * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ *
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ public static final String EXTRA_IS_FAILOVER = "isFailover";
+ /**
+ * The lookup key for a {@link NetworkInfo} object. This is supplied when
+ * there is another network that it may be possible to connect to. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ *
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+ /**
+ * The lookup key for a boolean that indicates whether there is a
+ * complete lack of connectivity, i.e., no network is available.
+ * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ */
+ public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
+ /**
+ * The lookup key for a string that indicates why an attempt to connect
+ * to a network failed. The string has no particular structure. It is
+ * intended to be used in notifications presented to users. Retrieve
+ * it with {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_REASON = "reason";
+ /**
+ * The lookup key for a string that provides optionally supplied
+ * extra information about the network state. The information
+ * may be passed up from the lower networking layers, and its
+ * meaning may be specific to a particular network type. Retrieve
+ * it with {@link android.content.Intent#getStringExtra(String)}.
+ *
+ * @deprecated See {@link NetworkInfo#getExtraInfo()}.
+ */
+ @Deprecated
+ public static final String EXTRA_EXTRA_INFO = "extraInfo";
+ /**
+ * The lookup key for an int that provides information about
+ * our connection to the internet at large. 0 indicates no connection,
+ * 100 indicates a great connection. Retrieve it with
+ * {@link android.content.Intent#getIntExtra(String, int)}.
+ * {@hide}
+ */
+ public static final String EXTRA_INET_CONDITION = "inetCondition";
+ /**
+ * The lookup key for a {@link CaptivePortal} object included with the
+ * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} intent. The {@code CaptivePortal}
+ * object can be used to either indicate to the system that the captive
+ * portal has been dismissed or that the user does not want to pursue
+ * signing in to captive portal. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+
+ /**
+ * Key for passing a URL to the captive portal login activity.
+ */
+ public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+
+ /**
+ * Key for passing a {@link android.net.captiveportal.CaptivePortalProbeSpec} to the captive
+ * portal login activity.
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC =
+ "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+
+ /**
+ * Key for passing a user agent string to the captive portal login activity.
+ * {@hide}
+ */
+ @SystemApi
+ public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT =
+ "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+
+ /**
+ * Broadcast action to indicate the change of data activity status
+ * (idle or active) on a network in a recent period.
+ * The network becomes active when data transmission is started, or
+ * idle if there is no data transmission for a period of time.
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DATA_ACTIVITY_CHANGE =
+ "android.net.conn.DATA_ACTIVITY_CHANGE";
+ /**
+ * The lookup key for an enum that indicates the network device type on which this data activity
+ * change happens.
+ * {@hide}
+ */
+ public static final String EXTRA_DEVICE_TYPE = "deviceType";
+ /**
+ * The lookup key for a boolean that indicates the device is active or not. {@code true} means
+ * it is actively sending or receiving data and {@code false} means it is idle.
+ * {@hide}
+ */
+ public static final String EXTRA_IS_ACTIVE = "isActive";
+ /**
+ * The lookup key for a long that contains the timestamp (nanos) of the radio state change.
+ * {@hide}
+ */
+ public static final String EXTRA_REALTIME_NS = "tsNanos";
+
+ /**
+ * Broadcast Action: The setting for background data usage has changed
+ * values. Use {@link #getBackgroundDataSetting()} to get the current value.
+ * <p>
+ * If an application uses the network in the background, it should listen
+ * for this broadcast and stop using the background data if the value is
+ * {@code false}.
+ * <p>
+ *
+ * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, availability
+ * of background data depends on several combined factors, and
+ * this broadcast is no longer sent. Instead, when background
+ * data is unavailable, {@link #getActiveNetworkInfo()} will now
+ * appear disconnected. During first boot after a platform
+ * upgrade, this broadcast will be sent once if
+ * {@link #getBackgroundDataSetting()} was {@code false} before
+ * the upgrade.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
+ public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
+ "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+
+ /**
+ * Broadcast Action: The network connection may not be good
+ * uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and
+ * {@code ConnectivityManager.EXTRA_NETWORK_INFO} to specify
+ * the network and it's condition.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @UnsupportedAppUsage
+ public static final String INET_CONDITION_ACTION =
+ "android.net.conn.INET_CONDITION_ACTION";
+
+ /**
+ * Broadcast Action: A tetherable connection has come or gone.
+ * Uses {@code ConnectivityManager.EXTRA_AVAILABLE_TETHER},
+ * {@code ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY},
+ * {@code ConnectivityManager.EXTRA_ACTIVE_TETHER}, and
+ * {@code ConnectivityManager.EXTRA_ERRORED_TETHER} to indicate
+ * the current state of tethering. Each include a list of
+ * interface names in that state (may be empty).
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final String ACTION_TETHER_STATE_CHANGED =
+ TetheringManager.ACTION_TETHER_STATE_CHANGED;
+
+ /**
+ * @hide
+ * gives a String[] listing all the interfaces configured for
+ * tethering and currently available for tethering.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ 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 = TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
+
+ /**
+ * @hide
+ * gives a String[] listing all the interfaces currently tethered
+ * (ie, has DHCPv4 support and packets potentially forwarded/NATed)
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final String EXTRA_ACTIVE_TETHER = TetheringManager.EXTRA_ACTIVE_TETHER;
+
+ /**
+ * @hide
+ * gives a String[] listing all the interfaces we tried to tether and
+ * failed. Use {@link #getLastTetherError} to find the error code
+ * for any interfaces listed here.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final String EXTRA_ERRORED_TETHER = TetheringManager.EXTRA_ERRORED_TETHER;
+
+ /**
+ * Broadcast Action: The captive portal tracker has finished its test.
+ * Sent only while running Setup Wizard, in lieu of showing a user
+ * notification.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CAPTIVE_PORTAL_TEST_COMPLETED =
+ "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED";
+ /**
+ * The lookup key for a boolean that indicates whether a captive portal was detected.
+ * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ * @hide
+ */
+ public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal";
+
+ /**
+ * Action used to display a dialog that asks the user whether to connect to a network that is
+ * not validated. This intent is used to start the dialog in settings via startActivity.
+ *
+ * @hide
+ */
+ public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
+
+ /**
+ * Action used to display a dialog that asks the user whether to avoid a network that is no
+ * longer validated. This intent is used to start the dialog in settings via startActivity.
+ *
+ * @hide
+ */
+ public static final String ACTION_PROMPT_LOST_VALIDATION =
+ "android.net.conn.PROMPT_LOST_VALIDATION";
+
+ /**
+ * Action used to display a dialog that asks the user whether to stay connected to a network
+ * that has not validated. This intent is used to start the dialog in settings via
+ * startActivity.
+ *
+ * @hide
+ */
+ public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY =
+ "android.net.conn.PROMPT_PARTIAL_CONNECTIVITY";
+
+ /**
+ * Invalid tethering type.
+ * @see #startTethering(int, boolean, OnStartTetheringCallback)
+ * @hide
+ */
+ public static final int TETHERING_INVALID = TetheringManager.TETHERING_INVALID;
+
+ /**
+ * Wifi tethering type.
+ * @see #startTethering(int, boolean, OnStartTetheringCallback)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_WIFI = TetheringManager.TETHERING_WIFI;
+
+ /**
+ * USB tethering type.
+ * @see #startTethering(int, boolean, OnStartTetheringCallback)
+ * @hide
+ */
+ @SystemApi
+ public static final int TETHERING_USB = TetheringManager.TETHERING_USB;
+
+ /**
+ * Bluetooth tethering type.
+ * @see #startTethering(int, boolean, OnStartTetheringCallback)
+ * @hide
+ */
+ @SystemApi
+ 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 = 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 = 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 = TetheringConstants.EXTRA_SET_ALARM;
+
+ /**
+ * Tells the TetherService to run a provision check now.
+ * @hide
+ */
+ 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 =
+ TetheringConstants.EXTRA_PROVISION_CALLBACK;
+
+ /**
+ * The absence of a connection type.
+ * @hide
+ */
+ @SystemApi
+ public static final int TYPE_NONE = -1;
+
+ /**
+ * A Mobile data connection. Devices may support more than one.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_MOBILE = 0;
+
+ /**
+ * A WIFI data connection. Devices may support more than one.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_WIFI = 1;
+
+ /**
+ * An MMS-specific Mobile data connection. This network type may use the
+ * same network interface as {@link #TYPE_MOBILE} or it may use a different
+ * one. This is used by applications needing to talk to the carrier's
+ * Multimedia Messaging Service servers.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
+ */
+ @Deprecated
+ public static final int TYPE_MOBILE_MMS = 2;
+
+ /**
+ * A SUPL-specific Mobile data connection. This network type may use the
+ * same network interface as {@link #TYPE_MOBILE} or it may use a different
+ * one. This is used by applications needing to talk to the carrier's
+ * Secure User Plane Location servers for help locating the device.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
+ */
+ @Deprecated
+ public static final int TYPE_MOBILE_SUPL = 3;
+
+ /**
+ * A DUN-specific Mobile data connection. This network type may use the
+ * same network interface as {@link #TYPE_MOBILE} or it may use a different
+ * one. This is sometimes by the system when setting up an upstream connection
+ * for tethering so that the carrier is aware of DUN traffic.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_DUN} capability.
+ */
+ @Deprecated
+ public static final int TYPE_MOBILE_DUN = 4;
+
+ /**
+ * A High Priority Mobile data connection. This network type uses the
+ * same network interface as {@link #TYPE_MOBILE} but the routing setup
+ * is different.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_MOBILE_HIPRI = 5;
+
+ /**
+ * A WiMAX data connection.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_WIMAX = 6;
+
+ /**
+ * A Bluetooth data connection.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_BLUETOOTH = 7;
+
+ /**
+ * Fake data connection. This should not be used on shipping devices.
+ * @deprecated This is not used any more.
+ */
+ @Deprecated
+ public static final int TYPE_DUMMY = 8;
+
+ /**
+ * An Ethernet data connection.
+ *
+ * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+ * appropriate network. {@see NetworkCapabilities} for supported transports.
+ */
+ @Deprecated
+ public static final int TYPE_ETHERNET = 9;
+
+ /**
+ * Over the air Administration.
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public static final int TYPE_MOBILE_FOTA = 10;
+
+ /**
+ * IP Multimedia Subsystem.
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IMS} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static final int TYPE_MOBILE_IMS = 11;
+
+ /**
+ * Carrier Branded Services.
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_CBS} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public static final int TYPE_MOBILE_CBS = 12;
+
+ /**
+ * A Wi-Fi p2p connection. Only requesting processes will have access to
+ * the peers connected.
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_WIFI_P2P} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @SystemApi
+ public static final int TYPE_WIFI_P2P = 13;
+
+ /**
+ * The network to use for initially attaching to the network
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IA} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static final int TYPE_MOBILE_IA = 14;
+
+ /**
+ * Emergency PDN connection for emergency services. This
+ * may include IMS and MMS in emergency situations.
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_EIMS} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public static final int TYPE_MOBILE_EMERGENCY = 15;
+
+ /**
+ * The network that uses proxy to achieve connectivity.
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @SystemApi
+ public static final int TYPE_PROXY = 16;
+
+ /**
+ * A virtual network using one or more native bearers.
+ * It may or may not be providing security services.
+ * @deprecated Applications should use {@link NetworkCapabilities#TRANSPORT_VPN} instead.
+ */
+ @Deprecated
+ public static final int TYPE_VPN = 17;
+
+ /**
+ * A network that is exclusively meant to be used for testing
+ *
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @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;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_TEST;
+
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
+ /**
+ * If you want to set the default network preference,you can directly
+ * change the networkAttributes array in framework's config.xml.
+ *
+ * @deprecated Since we support so many more networks now, the single
+ * network default network preference can't really express
+ * the hierarchy. Instead, the default is defined by the
+ * networkAttributes in config.xml. You can determine
+ * the current value by calling {@link #getNetworkPreference()}
+ * from an App.
+ */
+ @Deprecated
+ public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
+
+ /**
+ * @hide
+ */
+ public static final int REQUEST_ID_UNSET = 0;
+
+ /**
+ * Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
+ * This allows to distinguish when unregistering NetworkCallbacks those that were never
+ * registered from those that were already unregistered.
+ * @hide
+ */
+ private static final NetworkRequest ALREADY_UNREGISTERED =
+ new NetworkRequest.Builder().clearCapabilities().build();
+
+ /**
+ * A NetID indicating no Network is selected.
+ * Keep in sync with bionic/libc/dns/include/resolv_netid.h
+ * @hide
+ */
+ public static final int NETID_UNSET = 0;
+
+ /**
+ * Private DNS Mode values.
+ *
+ * The "private_dns_mode" global setting stores a String value which is
+ * expected to be one of the following.
+ */
+
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_OFF = "off";
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
+ /**
+ * The default Private DNS mode.
+ *
+ * This may change from release to release or may become dependent upon
+ * the capabilities of the underlying platform.
+ *
+ * @hide
+ */
+ public static final String PRIVATE_DNS_DEFAULT_MODE_FALLBACK = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ private final IConnectivityManager mService;
+ /**
+ * A kludge to facilitate static access where a Context pointer isn't available, like in the
+ * case of the static set/getProcessDefaultNetwork methods and from the Network class.
+ * TODO: Remove this after deprecating the static methods in favor of non-static methods or
+ * methods that take a Context argument.
+ */
+ private static ConnectivityManager sInstance;
+
+ private final Context mContext;
+
+ private INetworkManagementService mNMService;
+ private INetworkPolicyManager mNPManager;
+ private final TetheringManager mTetheringManager;
+
+ /**
+ * Tests if a given integer represents a valid network type.
+ * @param networkType the type to be tested
+ * @return a boolean. {@code true} if the type is valid, else {@code false}
+ * @deprecated All APIs accepting a network type are deprecated. There should be no need to
+ * validate a network type.
+ */
+ @Deprecated
+ public static boolean isNetworkTypeValid(int networkType) {
+ return MIN_NETWORK_TYPE <= networkType && networkType <= MAX_NETWORK_TYPE;
+ }
+
+ /**
+ * Returns a non-localized string representing a given network type.
+ * ONLY used for debugging output.
+ * @param type the type needing naming
+ * @return a String for the given type, or a string version of the type ("87")
+ * if no name is known.
+ * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
+ case TYPE_NONE:
+ return "NONE";
+ case TYPE_MOBILE:
+ return "MOBILE";
+ case TYPE_WIFI:
+ return "WIFI";
+ case TYPE_MOBILE_MMS:
+ return "MOBILE_MMS";
+ case TYPE_MOBILE_SUPL:
+ return "MOBILE_SUPL";
+ case TYPE_MOBILE_DUN:
+ return "MOBILE_DUN";
+ case TYPE_MOBILE_HIPRI:
+ return "MOBILE_HIPRI";
+ case TYPE_WIMAX:
+ return "WIMAX";
+ case TYPE_BLUETOOTH:
+ return "BLUETOOTH";
+ case TYPE_DUMMY:
+ return "DUMMY";
+ case TYPE_ETHERNET:
+ return "ETHERNET";
+ case TYPE_MOBILE_FOTA:
+ return "MOBILE_FOTA";
+ case TYPE_MOBILE_IMS:
+ return "MOBILE_IMS";
+ case TYPE_MOBILE_CBS:
+ return "MOBILE_CBS";
+ case TYPE_WIFI_P2P:
+ return "WIFI_P2P";
+ case TYPE_MOBILE_IA:
+ return "MOBILE_IA";
+ case TYPE_MOBILE_EMERGENCY:
+ return "MOBILE_EMERGENCY";
+ case TYPE_PROXY:
+ return "PROXY";
+ case TYPE_VPN:
+ return "VPN";
+ default:
+ return Integer.toString(type);
+ }
+ }
+
+ /**
+ * @hide
+ * TODO: Expose for SystemServer when becomes a module.
+ */
+ public void systemReady() {
+ try {
+ mService.systemReady();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks if a given type uses the cellular data connection.
+ * This should be replaced in the future by a network property.
+ * @param networkType the type to check
+ * @return a boolean - {@code true} if uses cellular network, else {@code false}
+ * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
+ * {@hide}
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public static boolean isNetworkTypeMobile(int networkType) {
+ switch (networkType) {
+ case TYPE_MOBILE:
+ case TYPE_MOBILE_MMS:
+ case TYPE_MOBILE_SUPL:
+ case TYPE_MOBILE_DUN:
+ case TYPE_MOBILE_HIPRI:
+ case TYPE_MOBILE_FOTA:
+ case TYPE_MOBILE_IMS:
+ case TYPE_MOBILE_CBS:
+ case TYPE_MOBILE_IA:
+ case TYPE_MOBILE_EMERGENCY:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given network type is backed by a Wi-Fi radio.
+ *
+ * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ public static boolean isNetworkTypeWifi(int networkType) {
+ switch (networkType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Specifies the preferred network type. When the device has more
+ * than one type available the preferred network type will be used.
+ *
+ * @param preference the network type to prefer over all others. It is
+ * unspecified what happens to the old preferred network in the
+ * overall ordering.
+ * @deprecated Functionality has been removed as it no longer makes sense,
+ * with many more than two networks - we'd need an array to express
+ * preference. Instead we use dynamic network properties of
+ * the networks to describe their precedence.
+ */
+ @Deprecated
+ public void setNetworkPreference(int preference) {
+ }
+
+ /**
+ * Retrieves the current preferred network type.
+ *
+ * @return an integer representing the preferred network type
+ *
+ * @deprecated Functionality has been removed as it no longer makes sense,
+ * with many more than two networks - we'd need an array to express
+ * preference. Instead we use dynamic network properties of
+ * the networks to describe their precedence.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public int getNetworkPreference() {
+ return TYPE_NONE;
+ }
+
+ /**
+ * Returns details about the currently active default data network. When
+ * connected, this network is the default route for outgoing connections.
+ * You should always check {@link NetworkInfo#isConnected()} before initiating
+ * network traffic. This may return {@code null} when there is no default
+ * network.
+ * Note that if the default network is a VPN, this method will return the
+ * NetworkInfo for one of its underlying networks instead, or null if the
+ * VPN agent did not specify any. Apps interested in learning about VPNs
+ * should use {@link #getNetworkInfo(android.net.Network)} instead.
+ *
+ * @return a {@link NetworkInfo} object for the current default network
+ * or {@code null} if no default network is currently active
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public NetworkInfo getActiveNetworkInfo() {
+ try {
+ return mService.getActiveNetworkInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a {@link Network} object corresponding to the currently active
+ * default data network. In the event that the current active default data
+ * network disconnects, the returned {@code Network} object will no longer
+ * be usable. This will return {@code null} when there is no default
+ * network.
+ *
+ * @return a {@link Network} object for the current default network or
+ * {@code null} if no default network is currently active
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public Network getActiveNetwork() {
+ try {
+ return mService.getActiveNetwork();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a {@link Network} object corresponding to the currently active
+ * default data network for a specific UID. In the event that the default data
+ * network disconnects, the returned {@code Network} object will no longer
+ * be usable. This will return {@code null} when there is no default
+ * network for the UID.
+ *
+ * @return a {@link Network} object for the current default network for the
+ * given UID or {@code null} if no default network is currently active
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ @Nullable
+ public Network getActiveNetworkForUid(int uid) {
+ return getActiveNetworkForUid(uid, false);
+ }
+
+ /** {@hide} */
+ public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
+ try {
+ return mService.getActiveNetworkForUid(uid, ignoreBlocked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks if a VPN app supports always-on mode.
+ *
+ * In order to support the always-on feature, an app has to
+ * <ul>
+ * <li>target {@link VERSION_CODES#N API 24} or above, and
+ * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+ * meta-data field.
+ * </ul>
+ *
+ * @param userId The identifier of the user for whom the VPN app is installed.
+ * @param vpnPackage The canonical package name of the VPN app.
+ * @return {@code true} if and only if the VPN app exists and supports always-on mode.
+ * @hide
+ */
+ public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
+ try {
+ return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Configures an always-on VPN connection through a specific application.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should declare a {@link VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param userId The identifier of the user to set an always-on VPN for.
+ * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration.
+ * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
+ * {@code false} otherwise.
+ * @param lockdownAllowlist The list of packages that are allowed to access network directly
+ * when VPN is in lockdown mode but is not running. Non-existent packages are ignored so
+ * this method must be called when a package that should be allowed is installed or
+ * uninstalled.
+ * @return {@code true} if the package is set as always-on VPN controller;
+ * {@code false} otherwise.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage,
+ boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) {
+ try {
+ return mService.setAlwaysOnVpnPackage(
+ userId, vpnPackage, lockdownEnabled, lockdownAllowlist);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the package name of the currently set always-on VPN application.
+ * If there is no always-on VPN set, or the VPN is provided by the system instead
+ * of by an app, {@code null} will be returned.
+ *
+ * @return Package name of VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public String getAlwaysOnVpnPackageForUser(int userId) {
+ try {
+ return mService.getAlwaysOnVpnPackage(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return whether always-on VPN is in lockdown mode.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public boolean isVpnLockdownEnabled(int userId) {
+ try {
+ return mService.isVpnLockdownEnabled(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ }
+
+ /**
+ * @return the list of packages that are allowed to access network when always-on VPN is in
+ * lockdown mode but not connected. Returns {@code null} when VPN lockdown is not active.
+ *
+ * @hide
+ **/
+ @RequiresPermission(android.Manifest.permission.CONTROL_ALWAYS_ON_VPN)
+ public List<String> getVpnLockdownWhitelist(int userId) {
+ try {
+ return mService.getVpnLockdownWhitelist(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds or removes a requirement for given UID ranges to use the VPN.
+ *
+ * If set to {@code true}, informs the system that the UIDs in the specified ranges must not
+ * have any connectivity except if a VPN is connected and applies to the UIDs, or if the UIDs
+ * otherwise have permission to bypass the VPN (e.g., because they have the
+ * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission, or when
+ * using a socket protected by a method such as {@link VpnService#protect(DatagramSocket)}. If
+ * set to {@code false}, a previously-added restriction is removed.
+ * <p>
+ * Each of the UID ranges specified by this method is added and removed as is, and no processing
+ * is performed on the ranges to de-duplicate, merge, split, or intersect them. In order to
+ * remove a previously-added range, the exact range must be removed as is.
+ * <p>
+ * The changes are applied asynchronously and may not have been applied by the time the method
+ * returns. Apps will be notified about any changes that apply to them via
+ * {@link NetworkCallback#onBlockedStatusChanged} callbacks called after the changes take
+ * effect.
+ * <p>
+ * This method should be called only by the VPN code.
+ *
+ * @param ranges the UID ranges to restrict
+ * @param requireVpn whether the specified UID ranges must use a VPN
+ *
+ * TODO: expose as @SystemApi.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public void setRequireVpnForUids(boolean requireVpn,
+ @NonNull Collection<Range<Integer>> ranges) {
+ Objects.requireNonNull(ranges);
+ // The Range class is not parcelable. Convert to UidRange, which is what is used internally.
+ // This method is not necessarily expected to be used outside the system server, so
+ // parceling may not be necessary, but it could be used out-of-process, e.g., by the network
+ // stack process, or by tests.
+ UidRange[] rangesArray = new UidRange[ranges.size()];
+ int index = 0;
+ for (Range<Integer> range : ranges) {
+ rangesArray[index++] = new UidRange(range.getLower(), range.getUpper());
+ }
+ try {
+ mService.setRequireVpnForUids(requireVpn, rangesArray);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns details about the currently active default data network
+ * for a given uid. This is for internal use only to avoid spying
+ * other apps.
+ *
+ * @return a {@link NetworkInfo} object for the current default network
+ * for the given uid or {@code null} if no default network is
+ * available for the specified uid.
+ *
+ * {@hide}
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+ return getActiveNetworkInfoForUid(uid, false);
+ }
+
+ /** {@hide} */
+ public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
+ try {
+ return mService.getActiveNetworkInfoForUid(uid, ignoreBlocked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns connection status information about a particular
+ * network type.
+ *
+ * @param networkType integer specifying which networkType in
+ * which you're interested.
+ * @return a {@link NetworkInfo} object for the requested
+ * network type or {@code null} if the type is not
+ * supported by the device. If {@code networkType} is
+ * TYPE_VPN and a VPN is active for the calling app,
+ * then this method will try to return one of the
+ * underlying networks for the VPN or null if the
+ * VPN agent didn't specify any.
+ *
+ * @deprecated This method does not support multiple connected networks
+ * of the same type. Use {@link #getAllNetworks} and
+ * {@link #getNetworkInfo(android.net.Network)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public NetworkInfo getNetworkInfo(int networkType) {
+ try {
+ return mService.getNetworkInfo(networkType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns connection status information about a particular
+ * Network.
+ *
+ * @param network {@link Network} specifying which network
+ * in which you're interested.
+ * @return a {@link NetworkInfo} object for the requested
+ * network or {@code null} if the {@code Network}
+ * is not valid.
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public NetworkInfo getNetworkInfo(@Nullable Network network) {
+ return getNetworkInfoForUid(network, Process.myUid(), false);
+ }
+
+ /** {@hide} */
+ public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
+ try {
+ return mService.getNetworkInfoForUid(network, uid, ignoreBlocked);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns connection status information about all network
+ * types supported by the device.
+ *
+ * @return an array of {@link NetworkInfo} objects. Check each
+ * {@link NetworkInfo#getType} for which type each applies.
+ *
+ * @deprecated This method does not support multiple connected networks
+ * of the same type. Use {@link #getAllNetworks} and
+ * {@link #getNetworkInfo(android.net.Network)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @NonNull
+ public NetworkInfo[] getAllNetworkInfo() {
+ try {
+ return mService.getAllNetworkInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the {@link Network} object currently serving a given type, or
+ * null if the given type is not connected.
+ *
+ * @hide
+ * @deprecated This method does not support multiple connected networks
+ * of the same type. Use {@link #getAllNetworks} and
+ * {@link #getNetworkInfo(android.net.Network)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @UnsupportedAppUsage
+ public Network getNetworkForType(int networkType) {
+ try {
+ return mService.getNetworkForType(networkType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns an array of all {@link Network} currently tracked by the
+ * framework.
+ *
+ * @return an array of {@link Network} objects.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @NonNull
+ public Network[] getAllNetworks() {
+ try {
+ return mService.getAllNetworks();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+ * the Networks that applications run by the given user will use by default.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ try {
+ return mService.getDefaultNetworkCapabilitiesForUser(
+ userId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the IP information for the current default network.
+ *
+ * @return a {@link LinkProperties} object describing the IP info
+ * for the current default network, or {@code null} if there
+ * is no current default network.
+ *
+ * {@hide}
+ * @deprecated please use {@link #getLinkProperties(Network)} on the return
+ * value of {@link #getActiveNetwork()} instead. In particular,
+ * this method will return non-null LinkProperties even if the
+ * app is blocked by policy from using this network.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 109783091)
+ public LinkProperties getActiveLinkProperties() {
+ try {
+ return mService.getActiveLinkProperties();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the IP information for a given network type.
+ *
+ * @param networkType the network type of interest.
+ * @return a {@link LinkProperties} object describing the IP info
+ * for the given networkType, or {@code null} if there is
+ * no current default network.
+ *
+ * {@hide}
+ * @deprecated This method does not support multiple connected networks
+ * of the same type. Use {@link #getAllNetworks},
+ * {@link #getNetworkInfo(android.net.Network)}, and
+ * {@link #getLinkProperties(android.net.Network)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public LinkProperties getLinkProperties(int networkType) {
+ try {
+ return mService.getLinkPropertiesForType(networkType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the {@link LinkProperties} for the given {@link Network}. This
+ * will return {@code null} if the network is unknown.
+ *
+ * @param network The {@link Network} object identifying the network in question.
+ * @return The {@link LinkProperties} for the network, or {@code null}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public LinkProperties getLinkProperties(@Nullable Network network) {
+ try {
+ return mService.getLinkProperties(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
+ * will return {@code null} if the network is unknown.
+ *
+ * @param network The {@link Network} object identifying the network in question.
+ * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
+ public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
+ try {
+ return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets a URL that can be used for resolving whether a captive portal is present.
+ * 1. This URL should respond with a 204 response to a GET request to indicate no captive
+ * portal is present.
+ * 2. This URL must be HTTP as redirect responses are used to find captive portal
+ * sign-in pages. Captive portals cannot respond to HTTPS requests with redirects.
+ *
+ * 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.NETWORK_SETTINGS)
+ public String getCaptivePortalServerUrl() {
+ try {
+ return mService.getCaptivePortalServerUrl();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Tells the underlying networking system that the caller wants to
+ * begin using the named feature. The interpretation of {@code feature}
+ * is completely up to each networking implementation.
+ *
+ * <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>
+ *
+ * @param networkType specifies which network the request pertains to
+ * @param feature the name of the feature to be used
+ * @return an integer value representing the outcome of the request.
+ * The interpretation of this value is specific to each networking
+ * implementation+feature combination, except that the value {@code -1}
+ * always indicates failure.
+ *
+ * @deprecated Deprecated in favor of the cleaner
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
+ * In {@link VERSION_CODES#M}, and above, this method is unsupported and will
+ * throw {@code UnsupportedOperationException} if called.
+ * @removed
+ */
+ @Deprecated
+ public int startUsingNetworkFeature(int networkType, String feature) {
+ checkLegacyRoutingApiAccess();
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " +
+ feature);
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
+ }
+
+ NetworkRequest request = null;
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) {
+ Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
+ renewRequestLocked(l);
+ if (l.currentNetwork != null) {
+ return DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE;
+ } else {
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
+ }
+ }
+
+ request = requestNetworkForFeatureLocked(netCap);
+ }
+ if (request != null) {
+ Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_STARTED;
+ } else {
+ Log.d(TAG, " request Failed");
+ return DEPRECATED_PHONE_CONSTANT_APN_REQUEST_FAILED;
+ }
+ }
+
+ /**
+ * Tells the underlying networking system that the caller is finished
+ * using the named feature. The interpretation of {@code feature}
+ * is completely up to each networking implementation.
+ *
+ * <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>
+ *
+ * @param networkType specifies which network the request pertains to
+ * @param feature the name of the feature that is no longer needed
+ * @return an integer value representing the outcome of the request.
+ * The interpretation of this value is specific to each networking
+ * implementation+feature combination, except that the value {@code -1}
+ * always indicates failure.
+ *
+ * @deprecated Deprecated in favor of the cleaner
+ * {@link #unregisterNetworkCallback(NetworkCallback)} API.
+ * In {@link VERSION_CODES#M}, and above, this method is unsupported and will
+ * throw {@code UnsupportedOperationException} if called.
+ * @removed
+ */
+ @Deprecated
+ public int stopUsingNetworkFeature(int networkType, String feature) {
+ checkLegacyRoutingApiAccess();
+ NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
+ if (netCap == null) {
+ Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " +
+ feature);
+ return -1;
+ }
+
+ if (removeRequestForFeature(netCap)) {
+ Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
+ }
+ return 1;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
+ if (networkType == TYPE_MOBILE) {
+ switch (feature) {
+ case "enableCBS":
+ return networkCapabilitiesForType(TYPE_MOBILE_CBS);
+ case "enableDUN":
+ case "enableDUNAlways":
+ return networkCapabilitiesForType(TYPE_MOBILE_DUN);
+ case "enableFOTA":
+ return networkCapabilitiesForType(TYPE_MOBILE_FOTA);
+ case "enableHIPRI":
+ return networkCapabilitiesForType(TYPE_MOBILE_HIPRI);
+ case "enableIMS":
+ return networkCapabilitiesForType(TYPE_MOBILE_IMS);
+ case "enableMMS":
+ return networkCapabilitiesForType(TYPE_MOBILE_MMS);
+ case "enableSUPL":
+ return networkCapabilitiesForType(TYPE_MOBILE_SUPL);
+ default:
+ return null;
+ }
+ } else if (networkType == TYPE_WIFI && "p2p".equals(feature)) {
+ return networkCapabilitiesForType(TYPE_WIFI_P2P);
+ }
+ return null;
+ }
+
+ private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
+ if (netCap == null) return TYPE_NONE;
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+ return TYPE_MOBILE_CBS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ return TYPE_MOBILE_IMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ return TYPE_MOBILE_FOTA;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ return TYPE_MOBILE_DUN;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ return TYPE_MOBILE_SUPL;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ return TYPE_MOBILE_MMS;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ return TYPE_MOBILE_HIPRI;
+ }
+ if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) {
+ return TYPE_WIFI_P2P;
+ }
+ return TYPE_NONE;
+ }
+
+ private static class LegacyRequest {
+ NetworkCapabilities networkCapabilities;
+ NetworkRequest networkRequest;
+ int expireSequenceNumber;
+ Network currentNetwork;
+ int delay = -1;
+
+ private void clearDnsBinding() {
+ if (currentNetwork != null) {
+ currentNetwork = null;
+ setProcessDefaultNetworkForHostResolution(null);
+ }
+ }
+
+ NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ currentNetwork = network;
+ Log.d(TAG, "startUsingNetworkFeature got Network:" + network);
+ setProcessDefaultNetworkForHostResolution(network);
+ }
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(currentNetwork)) clearDnsBinding();
+ Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
+ }
+ };
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private static final HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests =
+ new HashMap<>();
+
+ private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) {
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l != null) return l.networkRequest;
+ }
+ return null;
+ }
+
+ private void renewRequestLocked(LegacyRequest l) {
+ l.expireSequenceNumber++;
+ Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber);
+ sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay);
+ }
+
+ private void expireRequest(NetworkCapabilities netCap, int sequenceNum) {
+ int ourSeqNum = -1;
+ synchronized (sLegacyRequests) {
+ LegacyRequest l = sLegacyRequests.get(netCap);
+ if (l == null) return;
+ ourSeqNum = l.expireSequenceNumber;
+ if (l.expireSequenceNumber == sequenceNum) removeRequestForFeature(netCap);
+ }
+ Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum);
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) {
+ int delay = -1;
+ int type = legacyTypeForNetworkCapabilities(netCap);
+ try {
+ delay = mService.getRestoreDefaultNetworkDelay(type);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ LegacyRequest l = new LegacyRequest();
+ l.networkCapabilities = netCap;
+ l.delay = delay;
+ l.expireSequenceNumber = 0;
+ l.networkRequest = sendRequestForNetwork(
+ netCap, l.networkCallback, 0, REQUEST, type, getDefaultHandler());
+ if (l.networkRequest == null) return null;
+ sLegacyRequests.put(netCap, l);
+ sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
+ return l.networkRequest;
+ }
+
+ private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
+ if (delay >= 0) {
+ Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
+ CallbackHandler handler = getDefaultHandler();
+ Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+ handler.sendMessageDelayed(msg, delay);
+ }
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private boolean removeRequestForFeature(NetworkCapabilities netCap) {
+ final LegacyRequest l;
+ synchronized (sLegacyRequests) {
+ l = sLegacyRequests.remove(netCap);
+ }
+ if (l == null) return false;
+ unregisterNetworkCallback(l.networkCallback);
+ l.clearDnsBinding();
+ return true;
+ }
+
+ private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray();
+ static {
+ sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_CBS, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_FOTA, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_IMS, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_MMS, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_SUPL, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI);
+ sLegacyTypeToTransport.put(TYPE_WIFI_P2P, NetworkCapabilities.TRANSPORT_WIFI);
+ sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH);
+ sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET);
+ }
+
+ private static final SparseIntArray sLegacyTypeToCapability = new SparseIntArray();
+ static {
+ sLegacyTypeToCapability.put(TYPE_MOBILE_CBS, NetworkCapabilities.NET_CAPABILITY_CBS);
+ sLegacyTypeToCapability.put(TYPE_MOBILE_DUN, NetworkCapabilities.NET_CAPABILITY_DUN);
+ sLegacyTypeToCapability.put(TYPE_MOBILE_FOTA, NetworkCapabilities.NET_CAPABILITY_FOTA);
+ sLegacyTypeToCapability.put(TYPE_MOBILE_IMS, NetworkCapabilities.NET_CAPABILITY_IMS);
+ sLegacyTypeToCapability.put(TYPE_MOBILE_MMS, NetworkCapabilities.NET_CAPABILITY_MMS);
+ sLegacyTypeToCapability.put(TYPE_MOBILE_SUPL, NetworkCapabilities.NET_CAPABILITY_SUPL);
+ sLegacyTypeToCapability.put(TYPE_WIFI_P2P, NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+ }
+
+ /**
+ * Given a legacy type (TYPE_WIFI, ...) returns a NetworkCapabilities
+ * instance suitable for registering a request or callback. Throws an
+ * IllegalArgumentException if no mapping from the legacy type to
+ * NetworkCapabilities is known.
+ *
+ * @deprecated Types are deprecated. Use {@link NetworkCallback} or {@link NetworkRequest}
+ * to find the network instead.
+ * @hide
+ */
+ public static NetworkCapabilities networkCapabilitiesForType(int type) {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+
+ // Map from type to transports.
+ final int NOT_FOUND = -1;
+ final int transport = sLegacyTypeToTransport.get(type, NOT_FOUND);
+ Preconditions.checkArgument(transport != NOT_FOUND, "unknown legacy type: " + type);
+ nc.addTransportType(transport);
+
+ // Map from type to capabilities.
+ nc.addCapability(sLegacyTypeToCapability.get(
+ type, NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ nc.maybeMarkCapabilitiesRestricted();
+ return nc;
+ }
+
+ /** @hide */
+ public static class PacketKeepaliveCallback {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public PacketKeepaliveCallback() {
+ }
+ /** The requested keepalive was successfully started. */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public void onStopped() {}
+ /** An error occurred. */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public void onError(int error) {}
+ }
+
+ /**
+ * Allows applications to request that the system periodically send specific packets on their
+ * behalf, using hardware offload to save battery power.
+ *
+ * To request that the system send keepalives, call one of the methods that return a
+ * {@link ConnectivityManager.PacketKeepalive} object, such as {@link #startNattKeepalive},
+ * passing in a non-null callback. If the callback is successfully started, the callback's
+ * {@code onStarted} method will be called. If an error occurs, {@code onError} will be called,
+ * specifying one of the {@code ERROR_*} constants in this class.
+ *
+ * To stop an existing keepalive, call {@link PacketKeepalive#stop}. The system will call
+ * {@link PacketKeepaliveCallback#onStopped} if the operation was successful or
+ * {@link PacketKeepaliveCallback#onError} if an error occurred.
+ *
+ * @deprecated Use {@link SocketKeepalive} instead.
+ *
+ * @hide
+ */
+ public class PacketKeepalive {
+
+ private static final String TAG = "PacketKeepalive";
+
+ /** @hide */
+ public static final int SUCCESS = 0;
+
+ /** @hide */
+ public static final int NO_KEEPALIVE = -1;
+
+ /** @hide */
+ public static final int BINDER_DIED = -10;
+
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = -20;
+ /** The specified IP addresses are invalid. For example, the specified source IP address is
+ * not configured on the specified {@code Network}. */
+ public static final int ERROR_INVALID_IP_ADDRESS = -21;
+ /** The requested port is invalid. */
+ public static final int ERROR_INVALID_PORT = -22;
+ /** The packet length is invalid (e.g., too long). */
+ public static final int ERROR_INVALID_LENGTH = -23;
+ /** The packet transmission interval is invalid (e.g., too short). */
+ public static final int ERROR_INVALID_INTERVAL = -24;
+
+ /** The hardware does not support this request. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = -31;
+
+ /** The NAT-T destination port for IPsec */
+ public static final int NATT_PORT = 4500;
+
+ /** The minimum interval in seconds between keepalive packet transmissions */
+ public static final int MIN_INTERVAL = 10;
+
+ private final Network mNetwork;
+ private final ISocketKeepaliveCallback mCallback;
+ private final ExecutorService mExecutor;
+
+ private volatile Integer mSlot;
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public void stop() {
+ try {
+ mExecutor.execute(() -> {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ // The internal executor has already stopped due to previous event.
+ }
+ }
+
+ private PacketKeepalive(Network network, PacketKeepaliveCallback callback) {
+ Preconditions.checkNotNull(network, "network cannot be null");
+ Preconditions.checkNotNull(callback, "callback cannot be null");
+ mNetwork = network;
+ mExecutor = Executors.newSingleThreadExecutor();
+ mCallback = new ISocketKeepaliveCallback.Stub() {
+ @Override
+ public void onStarted(int slot) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = slot;
+ callback.onStarted();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = null;
+ callback.onStopped();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ mExecutor.shutdown();
+ }
+
+ @Override
+ public void onError(int error) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = null;
+ callback.onError(error);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ mExecutor.shutdown();
+ }
+
+ @Override
+ public void onDataReceived() {
+ // PacketKeepalive is only used for Nat-T keepalive and as such does not invoke
+ // this callback when data is received.
+ }
+ };
+ }
+ }
+
+ /**
+ * Starts an IPsec NAT-T keepalive packet with the specified parameters.
+ *
+ * @deprecated Use {@link #createSocketKeepalive} instead.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public PacketKeepalive startNattKeepalive(
+ Network network, int intervalSeconds, PacketKeepaliveCallback callback,
+ InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
+ final PacketKeepalive k = new PacketKeepalive(network, callback);
+ try {
+ mService.startNattKeepalive(network, intervalSeconds, k.mCallback,
+ srcAddr.getHostAddress(), srcPort, dstAddr.getHostAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ return k;
+ }
+
+ // Construct an invalid fd.
+ private ParcelFileDescriptor createInvalidFd() {
+ final int invalidFd = -1;
+ return ParcelFileDescriptor.adoptFd(invalidFd);
+ }
+
+ /**
+ * Request that keepalives be started on a IPsec NAT-T socket.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param socket The socket that needs to be kept alive.
+ * @param source The source address of the {@link UdpEncapsulationSocket}.
+ * @param destination The destination address of the {@link UdpEncapsulationSocket}.
+ * @param executor The executor on which callback will be invoked. The provided {@link Executor}
+ * must run callback sequentially, otherwise the order of callbacks cannot be
+ * guaranteed.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
+ **/
+ public @NonNull SocketKeepalive createSocketKeepalive(@NonNull Network network,
+ @NonNull UdpEncapsulationSocket socket,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ ParcelFileDescriptor dup;
+ try {
+ // Dup is needed here as the pfd inside the socket is owned by the IpSecService,
+ // which cannot be obtained by the app process.
+ dup = ParcelFileDescriptor.dup(socket.getFileDescriptor());
+ } catch (IOException ignored) {
+ // Construct an invalid fd, so that if the user later calls start(), it will fail with
+ // ERROR_INVALID_SOCKET.
+ dup = createInvalidFd();
+ }
+ return new NattSocketKeepalive(mService, network, dup, socket.getResourceId(), source,
+ destination, executor, callback);
+ }
+
+ /**
+ * Request that keepalives be started on a IPsec NAT-T socket file descriptor. Directly called
+ * by system apps which don't use IpSecService to create {@link UdpEncapsulationSocket}.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param pfd The {@link ParcelFileDescriptor} that needs to be kept alive. The provided
+ * {@link ParcelFileDescriptor} must be bound to a port and the keepalives will be sent
+ * from that port.
+ * @param source The source address of the {@link UdpEncapsulationSocket}.
+ * @param destination The destination address of the {@link UdpEncapsulationSocket}. The
+ * keepalive packets will always be sent to port 4500 of the given {@code destination}.
+ * @param executor The executor on which callback will be invoked. The provided {@link Executor}
+ * must run callback sequentially, otherwise the order of callbacks cannot be
+ * guaranteed.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+ public @NonNull SocketKeepalive createNattKeepalive(@NonNull Network network,
+ @NonNull ParcelFileDescriptor pfd,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Callback callback) {
+ ParcelFileDescriptor dup;
+ try {
+ // TODO: Consider remove unnecessary dup.
+ dup = pfd.dup();
+ } catch (IOException ignored) {
+ // Construct an invalid fd, so that if the user later calls start(), it will fail with
+ // ERROR_INVALID_SOCKET.
+ dup = createInvalidFd();
+ }
+ return new NattSocketKeepalive(mService, network, dup,
+ INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+ }
+
+ /**
+ * Request that keepalives be started on a TCP socket.
+ * The socket must be established.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param socket The socket that needs to be kept alive.
+ * @param executor The executor on which callback will be invoked. This implementation assumes
+ * the provided {@link Executor} runs the callbacks in sequence with no
+ * concurrency. Failing this, no guarantee of correctness can be made. It is
+ * the responsibility of the caller to ensure the executor provides this
+ * guarantee. A simple way of creating such an executor is with the standard
+ * tool {@code Executors.newSingleThreadExecutor}.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+ public @NonNull SocketKeepalive createSocketKeepalive(@NonNull Network network,
+ @NonNull Socket socket,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ ParcelFileDescriptor dup;
+ try {
+ dup = ParcelFileDescriptor.fromSocket(socket);
+ } catch (UncheckedIOException ignored) {
+ // Construct an invalid fd, so that if the user later calls start(), it will fail with
+ // ERROR_INVALID_SOCKET.
+ dup = createInvalidFd();
+ }
+ return new TcpSocketKeepalive(mService, network, dup, executor, callback);
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the specified network interface. An attempt to add a route that
+ * already exists is ignored, but treated as successful.
+ *
+ * <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>
+ *
+ * @param networkType the type of the network over which traffic to the specified
+ * host is to be routed
+ * @param hostAddress the IP address of the host to which the route is desired
+ * @return {@code true} on success, {@code false} on failure
+ *
+ * @deprecated Deprecated in favor of the
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)},
+ * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
+ * In {@link VERSION_CODES#M}, and above, this method is unsupported and will
+ * throw {@code UnsupportedOperationException} if called.
+ * @removed
+ */
+ @Deprecated
+ public boolean requestRouteToHost(int networkType, int hostAddress) {
+ return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
+ }
+
+ /**
+ * Ensure that a network route exists to deliver traffic to the specified
+ * host via the specified network interface. An attempt to add a route that
+ * already exists is ignored, but treated as successful.
+ *
+ * <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>
+ *
+ * @param networkType the type of the network over which traffic to the specified
+ * host is to be routed
+ * @param hostAddress the IP address of the host to which the route is desired
+ * @return {@code true} on success, {@code false} on failure
+ * @hide
+ * @deprecated Deprecated in favor of the {@link #requestNetwork} and
+ * {@link #bindProcessToNetwork} API.
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
+ checkLegacyRoutingApiAccess();
+ try {
+ return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress(),
+ mContext.getOpPackageName(), getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return the context's attribution tag
+ */
+ // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+ private @Nullable String getAttributionTag() {
+ return null;
+ }
+
+ /**
+ * Returns the value of the setting for background data usage. If false,
+ * applications should not use the network if the application is not in the
+ * foreground. Developers should respect this setting, and check the value
+ * of this before performing any background data operations.
+ * <p>
+ * All applications that have background services that use the network
+ * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+ * <p>
+ * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, availability of
+ * background data depends on several combined factors, and this method will
+ * always return {@code true}. Instead, when background data is unavailable,
+ * {@link #getActiveNetworkInfo()} will now appear disconnected.
+ *
+ * @return Whether background data usage is allowed.
+ */
+ @Deprecated
+ public boolean getBackgroundDataSetting() {
+ // assume that background data is allowed; final authority is
+ // NetworkInfo which may be blocked.
+ return true;
+ }
+
+ /**
+ * Sets the value of the setting for background data usage.
+ *
+ * @param allowBackgroundData Whether an application should use data while
+ * it is in the background.
+ *
+ * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING
+ * @see #getBackgroundDataSetting()
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public void setBackgroundDataSetting(boolean allowBackgroundData) {
+ // ignored
+ }
+
+ /**
+ * @hide
+ * @deprecated Talk to TelephonyManager directly
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public boolean getMobileDataEnabled() {
+ 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;
+ }
+
+ /**
+ * Callback for use with {@link ConnectivityManager#addDefaultNetworkActiveListener}
+ * to find out when the system default network has gone in to a high power state.
+ */
+ public interface OnNetworkActiveListener {
+ /**
+ * Called on the main thread of the process to report that the current data network
+ * has become active, and it is now a good time to perform any pending network
+ * operations. Note that this listener only tells you when the network becomes
+ * active; if at any other time you want to know whether it is active (and thus okay
+ * to initiate network traffic), you can retrieve its instantaneous state with
+ * {@link ConnectivityManager#isDefaultNetworkActive}.
+ */
+ void onNetworkActive();
+ }
+
+ private INetworkManagementService getNetworkManagementService() {
+ synchronized (this) {
+ if (mNMService != null) {
+ return mNMService;
+ }
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ mNMService = INetworkManagementService.Stub.asInterface(b);
+ return mNMService;
+ }
+ }
+
+ private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
+ mNetworkActivityListeners = new ArrayMap<>();
+
+ /**
+ * Start listening to reports when the system's default data network is active, meaning it is
+ * a good time to perform network traffic. Use {@link #isDefaultNetworkActive()}
+ * to determine the current state of the system's default network after registering the
+ * listener.
+ * <p>
+ * If the process default network has been set with
+ * {@link ConnectivityManager#bindProcessToNetwork} this function will not
+ * reflect the process's default, but the system default.
+ *
+ * @param l The listener to be told when the network is active.
+ */
+ public void addDefaultNetworkActiveListener(final OnNetworkActiveListener l) {
+ INetworkActivityListener rl = new INetworkActivityListener.Stub() {
+ @Override
+ public void onNetworkActive() throws RemoteException {
+ l.onNetworkActive();
+ }
+ };
+
+ try {
+ getNetworkManagementService().registerNetworkActivityListener(rl);
+ mNetworkActivityListeners.put(l, rl);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove network active listener previously registered with
+ * {@link #addDefaultNetworkActiveListener}.
+ *
+ * @param l Previously registered listener.
+ */
+ public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
+ INetworkActivityListener rl = mNetworkActivityListeners.get(l);
+ Preconditions.checkArgument(rl != null, "Listener was not registered.");
+ try {
+ getNetworkManagementService().unregisterNetworkActivityListener(rl);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return whether the data network is currently active. An active network means that
+ * it is currently in a high power state for performing data transmission. On some
+ * types of networks, it may be expensive to move and stay in such a state, so it is
+ * more power efficient to batch network traffic together when the radio is already in
+ * this state. This method tells you whether right now is currently a good time to
+ * initiate network traffic, as the network is already active.
+ */
+ public boolean isDefaultNetworkActive() {
+ try {
+ return getNetworkManagementService().isNetworkActive();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ 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;
+ }
+
+ /** {@hide} */
+ @UnsupportedAppUsage
+ public static ConnectivityManager from(Context context) {
+ return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ /** @hide */
+ public NetworkRequest getDefaultRequest() {
+ try {
+ // This is not racy as the default request is final in ConnectivityService.
+ return mService.getDefaultRequest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /* TODO: These permissions checks don't belong in client-side code. Move them to
+ * services.jar, possibly in com.android.server.net. */
+
+ /** {@hide} */
+ public static final void enforceChangePermission(Context context,
+ String callingPkg, String callingAttributionTag) {
+ int uid = Binder.getCallingUid();
+ checkAndNoteChangeNetworkStateOperation(context, uid, callingPkg,
+ callingAttributionTag, true /* throwException */);
+ }
+
+ /**
+ * Check if the package is a allowed to change the network state. This also accounts that such
+ * an access happened.
+ *
+ * @return {@code true} iff the package is allowed to change the network state.
+ */
+ // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+ private static boolean checkAndNoteChangeNetworkStateOperation(@NonNull Context context,
+ int uid, @NonNull String callingPackage, @Nullable String callingAttributionTag,
+ boolean throwException) {
+ return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, callingPackage,
+ throwException);
+ }
+
+ /**
+ * Check if the package is a allowed to write settings. This also accounts that such an access
+ * happened.
+ *
+ * @return {@code true} iff the package is allowed to write settings.
+ */
+ // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+ private static boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+ @NonNull String callingPackage, @Nullable String callingAttributionTag,
+ boolean throwException) {
+ return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
+ throwException);
+ }
+
+ /**
+ * @deprecated - use getSystemService. This is a kludge to support static access in certain
+ * situations where a Context pointer is unavailable.
+ * @hide
+ */
+ @Deprecated
+ static ConnectivityManager getInstanceOrNull() {
+ return sInstance;
+ }
+
+ /**
+ * @deprecated - use getSystemService. This is a kludge to support static access in certain
+ * situations where a Context pointer is unavailable.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ private static ConnectivityManager getInstance() {
+ if (getInstanceOrNull() == null) {
+ throw new IllegalStateException("No ConnectivityManager yet constructed");
+ }
+ return getInstanceOrNull();
+ }
+
+ /**
+ * Get the set of tetherable, available interfaces. This list is limited by
+ * device configuration and current interface existence.
+ *
+ * @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() {
+ return mTetheringManager.getTetherableIfaces();
+ }
+
+ /**
+ * Get the set of tethered interfaces.
+ *
+ * @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() {
+ return mTetheringManager.getTetheredIfaces();
+ }
+
+ /**
+ * Get the set of interface names which attempted to tether but
+ * failed. Re-attempting to tether may cause them to reset to the Tethered
+ * state. Alternatively, causing the interface to be destroyed and recreated
+ * may cause them to reset to the available state.
+ * {@link ConnectivityManager#getLastTetherError} can be used to get more
+ * information on the cause of the errors.
+ *
+ * @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() {
+ return mTetheringManager.getTetheringErroredIfaces();
+ }
+
+ /**
+ * Get the set 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() {
+ throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
+ }
+
+ /**
+ * Attempt to tether the named interface. This will setup a dhcp server
+ * on the interface, forward and NAT IP packets and forward DNS requests
+ * to the best active upstream network interface. Note that if no upstream
+ * IP network interface is available, dhcp will still run and traffic will be
+ * allowed between the tethered devices and this device, though upstream net
+ * access will of course fail until an upstream network interface becomes
+ * active.
+ *
+ * <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>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
+ * @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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
+ public int tether(String iface) {
+ return mTetheringManager.tether(iface);
+ }
+
+ /**
+ * Stop tethering the named interface.
+ *
+ * <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>WARNING: New clients should not use this function. The only usages should be in PanService
+ * and WifiStateMachine which need direct access. All other clients should use
+ * {@link #startTethering} and {@link #stopTethering} which encapsulate proper provisioning
+ * logic.</p>
+ *
+ * @param iface the interface name to untether.
+ * @return error a {@code TETHER_ERROR} value indicating success or failure type
+ *
+ * {@hide}
+ */
+ @UnsupportedAppUsage
+ @Deprecated
+ public int untether(String iface) {
+ return mTetheringManager.untether(iface);
+ }
+
+ /**
+ * Check if the device allows for tethering. It may be disabled via
+ * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
+ * due to device configuration.
+ *
+ * <p>If this app does not have permission to use this API, it will always
+ * return false rather than throw an exception.</p>
+ *
+ * <p>If the device has a hotspot provisioning app, the caller is required to hold the
+ * {@link android.Manifest.permission.TETHER_PRIVILEGED} permission.</p>
+ *
+ * <p>Otherwise, this method requires the caller to hold the ability to modify system
+ * settings as determined by {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @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() {
+ 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.
+ */
+ public void onTetheringStarted() {}
+
+ /**
+ * Called when starting tethering failed.
+ */
+ public void onTetheringFailed() {}
+ }
+
+ /**
+ * 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) {
+ startTethering(type, showProvisioningUi, callback, null);
+ }
+
+ /**
+ * Runs tether provisioning for the given type if needed and then starts tethering if
+ * the check succeeds. If no carrier provisioning is required for tethering, tethering is
+ * enabled immediately. If provisioning fails, tethering will not be enabled. It also
+ * schedules tether provisioning re-checks if appropriate.
+ *
+ * @param type The type of tethering to start. Must be one of
+ * {@link ConnectivityManager.TETHERING_WIFI},
+ * {@link ConnectivityManager.TETHERING_USB}, or
+ * {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
+ * is one. This should be true the first time this function is called and also any time
+ * the user can see this UI. It gives users information from their carrier about the
+ * check failing and how they can sign up for tethering if possible.
+ * @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.");
+
+ final Executor executor = new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ if (handler == null) {
+ command.run();
+ } else {
+ handler.post(command);
+ }
+ }
+ };
+
+ 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);
+ }
+
+ /**
+ * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+ * applicable.
+ *
+ * @param type The type of tethering to stop. Must be one of
+ * {@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) {
+ mTetheringManager.stopTethering(type);
+ }
+
+ /**
+ * Callback for use with {@link registerTetheringEventCallback} to find out tethering
+ * upstream status.
+ *
+ * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ public abstract static class OnTetheringEventCallback {
+
+ /**
+ * Called when tethering upstream changed. This can be called multiple times and can be
+ * called any time.
+ *
+ * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
+ * have any upstream.
+ */
+ public void onUpstreamChanged(@Nullable Network network) {}
+ }
+
+ @GuardedBy("mTetheringEventCallbacks")
+ private final ArrayMap<OnTetheringEventCallback, TetheringEventCallback>
+ mTetheringEventCallbacks = new ArrayMap<>();
+
+ /**
+ * Start listening to tethering change events. Any new added callback will receive the last
+ * tethering status right away. If callback is registered when tethering has no upstream or
+ * disabled, {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called
+ * with a null argument. The same callback object cannot be registered twice.
+ *
+ * @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) {
+ mTetheringEventCallbacks.put(callback, tetherCallback);
+ mTetheringManager.registerTetheringEventCallback(executor, tetherCallback);
+ }
+ }
+
+ /**
+ * Remove tethering event callback previously registered with
+ * {@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) {
+ final TetheringEventCallback tetherCallback =
+ mTetheringEventCallbacks.remove(callback);
+ mTetheringManager.unregisterTetheringEventCallback(tetherCallback);
+ }
+ }
+
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * USB network interfaces. If USB tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * @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() {
+ return mTetheringManager.getTetherableUsbRegexs();
+ }
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * Wifi network interfaces. If Wifi tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * @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() {
+ return mTetheringManager.getTetherableWifiRegexs();
+ }
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * Bluetooth network interfaces. If Bluetooth tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * @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() {
+ return mTetheringManager.getTetherableBluetoothRegexs();
+ }
+
+ /**
+ * Attempt to both alter the mode of USB and Tethering of USB. A
+ * utility method to deal with some of the complexity of USB - will
+ * attempt to switch to Rndis and subsequently tether the resulting
+ * interface on {@code true} or turn off tethering and switch off
+ * Rndis on {@code false}.
+ *
+ * <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>
+ *
+ * @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) {
+ return mTetheringManager.setUsbTethering(enable);
+ }
+
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}.
+ * {@hide}
+ */
+ @SystemApi
+ @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
+ @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
+ @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
+ * request asynchronously failed.
+ *
+ * @param iface The name of the interface of interest
+ * @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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
+ public int getLastTetherError(String iface) {
+ 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 */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ TETHER_ERROR_NO_ERROR,
+ TETHER_ERROR_PROVISION_FAILED,
+ TETHER_ERROR_ENTITLEMENT_UNKONWN,
+ })
+ public @interface EntitlementResultCode {
+ }
+
+ /**
+ * 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.
+ *
+ * @param resultCode an int value of entitlement result. It may be one of
+ * {@link #TETHER_ERROR_NO_ERROR},
+ * {@link #TETHER_ERROR_PROVISION_FAILED}, or
+ * {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}.
+ */
+ void onTetheringEntitlementResult(@EntitlementResultCode int resultCode);
+ }
+
+ /**
+ * Get the last value of the entitlement check on this downstream. If the cached value is
+ * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
+ * cached value. Otherwise, a UI-based entitlement check would be performed. It is not
+ * guaranteed that the UI-based entitlement check will complete in any specific time period
+ * and may in fact never complete. Any successful entitlement check the platform performs for
+ * any reason will update the cached value.
+ *
+ * @param type the downstream type of tethering. Must be one of
+ * {@link #TETHERING_WIFI},
+ * {@link #TETHERING_USB}, or
+ * {@link #TETHERING_BLUETOOTH}.
+ * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+ * @param executor the executor on which callback will be invoked.
+ * @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,
+ @NonNull final OnTetheringEntitlementResultListener listener) {
+ Preconditions.checkNotNull(listener, "TetheringEntitlementResultListener cannot be null.");
+ ResultReceiver wrappedListener = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() -> {
+ listener.onTetheringEntitlementResult(resultCode);
+ }));
+ }
+ };
+
+ mTetheringManager.requestLatestTetheringEntitlementResult(type, wrappedListener,
+ showEntitlementUi);
+ }
+
+ /**
+ * Report network connectivity status. This is currently used only
+ * to alter status bar UI.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#STATUS_BAR}.
+ *
+ * @param networkType The type of network you want to report on
+ * @param percentage The quality of the connection 0 is bad, 100 is good
+ * @deprecated Types are deprecated. Use {@link #reportNetworkConnectivity} instead.
+ * {@hide}
+ */
+ public void reportInetCondition(int networkType, int percentage) {
+ printStackTrace();
+ try {
+ mService.reportInetCondition(networkType, percentage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Report a problem network to the framework. This provides a hint to the system
+ * that there might be connectivity problems on this network and may cause
+ * the framework to re-evaluate network connectivity and/or switch to another
+ * network.
+ *
+ * @param network The {@link Network} the application was attempting to use
+ * or {@code null} to indicate the current default network.
+ * @deprecated Use {@link #reportNetworkConnectivity} which allows reporting both
+ * working and non-working connectivity.
+ */
+ @Deprecated
+ public void reportBadNetwork(@Nullable Network network) {
+ printStackTrace();
+ try {
+ // One of these will be ignored because it matches system's current state.
+ // The other will trigger the necessary reevaluation.
+ mService.reportNetworkConnectivity(network, true);
+ mService.reportNetworkConnectivity(network, false);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Report to the framework whether a network has working connectivity.
+ * This provides a hint to the system that a particular network is providing
+ * working connectivity or not. In response the framework may re-evaluate
+ * the network's connectivity and might take further action thereafter.
+ *
+ * @param network The {@link Network} the application was attempting to use
+ * or {@code null} to indicate the current default network.
+ * @param hasConnectivity {@code true} if the application was able to successfully access the
+ * Internet using {@code network} or {@code false} if not.
+ */
+ public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
+ printStackTrace();
+ try {
+ mService.reportNetworkConnectivity(network, hasConnectivity);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set a network-independent global http proxy. This is not normally what you want
+ * for typical HTTP proxies - they are general network dependent. However if you're
+ * doing something unusual like general internal filtering this may be useful. On
+ * a private network where the proxy is not accessible, you may break HTTP using this.
+ *
+ * @param p A {@link ProxyInfo} object defining the new global
+ * HTTP proxy. A {@code null} value will clear the global HTTP proxy.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setGlobalProxy(ProxyInfo p) {
+ try {
+ mService.setGlobalProxy(p);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieve any network-independent global HTTP proxy.
+ *
+ * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
+ * if no global HTTP proxy is set.
+ * @hide
+ */
+ public ProxyInfo getGlobalProxy() {
+ try {
+ return mService.getGlobalProxy();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieve the global HTTP proxy, or if no global HTTP proxy is set, a
+ * network-specific HTTP proxy. If {@code network} is null, the
+ * network-specific proxy returned is the proxy of the default active
+ * network.
+ *
+ * @return {@link ProxyInfo} for the current global HTTP proxy, or if no
+ * global HTTP proxy is set, {@code ProxyInfo} for {@code network},
+ * or when {@code network} is {@code null},
+ * the {@code ProxyInfo} for the default active network. Returns
+ * {@code null} when no proxy applies or the caller doesn't have
+ * permission to use {@code network}.
+ * @hide
+ */
+ public ProxyInfo getProxyForNetwork(Network network) {
+ try {
+ return mService.getProxyForNetwork(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current default HTTP proxy settings. If a global proxy is set it will be returned,
+ * otherwise if this process is bound to a {@link Network} using
+ * {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
+ * the default network's proxy is returned.
+ *
+ * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
+ * HTTP proxy is active.
+ */
+ @Nullable
+ public ProxyInfo getDefaultProxy() {
+ return getProxyForNetwork(getBoundNetworkForProcess());
+ }
+
+ /**
+ * Returns true if the hardware supports the given network type
+ * else it returns false. This doesn't indicate we have coverage
+ * or are authorized onto a network, just whether or not the
+ * hardware supports it. For example a GSM phone without a SIM
+ * should still return {@code true} for mobile data, but a wifi only
+ * tablet would return {@code false}.
+ *
+ * @param networkType The network type we'd like to check
+ * @return {@code true} if supported, else {@code false}
+ * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ public boolean isNetworkSupported(int networkType) {
+ try {
+ return mService.isNetworkSupported(networkType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns if the currently active data network is metered. A network is
+ * classified as metered when the user is sensitive to heavy data usage on
+ * that connection due to monetary costs, data limitations or
+ * battery/performance issues. You should check this before doing large
+ * data transfers, and warn the user or delay the operation until another
+ * network is available.
+ *
+ * @return {@code true} if large transfers should be avoided, otherwise
+ * {@code false}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public boolean isActiveNetworkMetered() {
+ try {
+ return mService.isActiveNetworkMetered();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * If the LockdownVpn mechanism is enabled, updates the vpn
+ * with a reload of its profile.
+ *
+ * @return a boolean with {@code} indicating success
+ *
+ * <p>This method can only be called by the system UID
+ * {@hide}
+ */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set sign in error notification to visible or invisible
+ *
+ * @hide
+ * @deprecated Doesn't properly deal with multiple connected networks of the same type.
+ */
+ @Deprecated
+ public void setProvisioningNotificationVisible(boolean visible, int networkType,
+ String action) {
+ try {
+ mService.setProvisioningNotificationVisible(visible, networkType, action);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the value for enabling/disabling airplane mode
+ *
+ * @param enable whether to enable airplane mode or not
+ *
+ * @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})
+ @SystemApi
+ public void setAirplaneMode(boolean enable) {
+ try {
+ mService.setAirplaneMode(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@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);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@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);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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
+ // the entire tree.
+ /**
+ * @hide
+ * Register a NetworkAgent with ConnectivityService.
+ * @return Network corresponding to NetworkAgent.
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+ return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
+ }
+
+ /**
+ * @hide
+ * Register a NetworkAgent with ConnectivityService.
+ * @return Network corresponding to NetworkAgent.
+ */
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
+ public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp,
+ NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+ try {
+ return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Base class for {@code NetworkRequest} callbacks. Used for notifications about network
+ * changes. Should be extended by applications wanting notifications.
+ *
+ * A {@code NetworkCallback} is registered by calling
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)},
+ * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)},
+ * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is
+ * unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
+ * A {@code NetworkCallback} should be registered at most once at any time.
+ * A {@code NetworkCallback} that has been unregistered can be registered again.
+ */
+ public static class NetworkCallback {
+ /**
+ * Called when the framework connects to a new network to evaluate whether it satisfies this
+ * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
+ * callback. There is no guarantee that this new network will satisfy any requests, or that
+ * the network will stay connected for longer than the time necessary to evaluate it.
+ * <p>
+ * Most applications <b>should not</b> act on this callback, and should instead use
+ * {@link #onAvailable}. This callback is intended for use by applications that can assist
+ * the framework in properly evaluating the network — for example, an application that
+ * can automatically log in to a captive portal without user intervention.
+ *
+ * @param network The {@link Network} of the network that is being evaluated.
+ *
+ * @hide
+ */
+ public void onPreCheck(@NonNull Network network) {}
+
+ /**
+ * 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.
+ *
+ * @param network The {@link Network} of the satisfying network.
+ * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+ * @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @param blocked Whether access to the {@link Network} is blocked due to system policy.
+ * @hide
+ */
+ public void onAvailable(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties, boolean blocked) {
+ // Internally only this method is called when a new network is available, and
+ // it calls the callback in the same way and order that older versions used
+ // to call so as not to change the behavior.
+ onAvailable(network);
+ if (!networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+ onNetworkSuspended(network);
+ }
+ onCapabilitiesChanged(network, networkCapabilities);
+ onLinkPropertiesChanged(network, linkProperties);
+ onBlockedStatusChanged(network, blocked);
+ }
+
+ /**
+ * Called when the framework connects and has declared a new network ready for use.
+ *
+ * <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 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.
+ *
+ * <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 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 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
+ * {@link NetworkRequest} will have already been removed and released, as if
+ * {@link #unregisterNetworkCallback(NetworkCallback)} had been called.
+ */
+ public void onUnavailable() {}
+
+ /**
+ * 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
+ * network.
+ */
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {}
+
+ /**
+ * 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.
+ */
+ public void onLinkPropertiesChanged(@NonNull Network network,
+ @NonNull LinkProperties linkProperties) {}
+
+ /**
+ * 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) {}
+
+ /**
+ * 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) {}
+
+ /**
+ * 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}.
+ */
+ public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
+
+ private NetworkRequest networkRequest;
+ }
+
+ /**
+ * Constant error codes used by ConnectivityService to communicate about failures and errors
+ * across a Binder boundary.
+ * @hide
+ */
+ public interface Errors {
+ int TOO_MANY_REQUESTS = 1;
+ }
+
+ /** @hide */
+ public static class TooManyRequestsException extends RuntimeException {}
+
+ private static RuntimeException convertServiceException(ServiceSpecificException e) {
+ switch (e.errorCode) {
+ case Errors.TOO_MANY_REQUESTS:
+ return new TooManyRequestsException();
+ default:
+ Log.w(TAG, "Unknown service error code " + e.errorCode);
+ return new RuntimeException(e);
+ }
+ }
+
+ private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
+ /** @hide */
+ public static final int CALLBACK_PRECHECK = BASE + 1;
+ /** @hide */
+ public static final int CALLBACK_AVAILABLE = BASE + 2;
+ /** @hide arg1 = TTL */
+ public static final int CALLBACK_LOSING = BASE + 3;
+ /** @hide */
+ public static final int CALLBACK_LOST = BASE + 4;
+ /** @hide */
+ public static final int CALLBACK_UNAVAIL = BASE + 5;
+ /** @hide */
+ public static final int CALLBACK_CAP_CHANGED = BASE + 6;
+ /** @hide */
+ public static final int CALLBACK_IP_CHANGED = BASE + 7;
+ /** @hide obj = NetworkCapabilities, arg1 = seq number */
+ private static final int EXPIRE_LEGACY_REQUEST = BASE + 8;
+ /** @hide */
+ public static final int CALLBACK_SUSPENDED = BASE + 9;
+ /** @hide */
+ public static final int CALLBACK_RESUMED = BASE + 10;
+ /** @hide */
+ public static final int CALLBACK_BLK_CHANGED = BASE + 11;
+
+ /** @hide */
+ public static String getCallbackName(int whichCallback) {
+ switch (whichCallback) {
+ case CALLBACK_PRECHECK: return "CALLBACK_PRECHECK";
+ case CALLBACK_AVAILABLE: return "CALLBACK_AVAILABLE";
+ case CALLBACK_LOSING: return "CALLBACK_LOSING";
+ case CALLBACK_LOST: return "CALLBACK_LOST";
+ case CALLBACK_UNAVAIL: return "CALLBACK_UNAVAIL";
+ case CALLBACK_CAP_CHANGED: return "CALLBACK_CAP_CHANGED";
+ case CALLBACK_IP_CHANGED: return "CALLBACK_IP_CHANGED";
+ case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
+ case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED";
+ case CALLBACK_RESUMED: return "CALLBACK_RESUMED";
+ case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED";
+ default:
+ return Integer.toString(whichCallback);
+ }
+ }
+
+ private class CallbackHandler extends Handler {
+ private static final String TAG = "ConnectivityManager.CallbackHandler";
+ private static final boolean DBG = false;
+
+ CallbackHandler(Looper looper) {
+ super(looper);
+ }
+
+ CallbackHandler(Handler handler) {
+ this(Preconditions.checkNotNull(handler, "Handler cannot be null.").getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == EXPIRE_LEGACY_REQUEST) {
+ expireRequest((NetworkCapabilities) message.obj, message.arg1);
+ return;
+ }
+
+ final NetworkRequest request = getObject(message, NetworkRequest.class);
+ final Network network = getObject(message, Network.class);
+ final NetworkCallback callback;
+ synchronized (sCallbacks) {
+ callback = sCallbacks.get(request);
+ if (callback == null) {
+ Log.w(TAG,
+ "callback not found for " + getCallbackName(message.what) + " message");
+ return;
+ }
+ if (message.what == CALLBACK_UNAVAIL) {
+ sCallbacks.remove(request);
+ callback.networkRequest = ALREADY_UNREGISTERED;
+ }
+ }
+ if (DBG) {
+ Log.d(TAG, getCallbackName(message.what) + " for network " + network);
+ }
+
+ switch (message.what) {
+ case CALLBACK_PRECHECK: {
+ callback.onPreCheck(network);
+ break;
+ }
+ case CALLBACK_AVAILABLE: {
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+ LinkProperties lp = getObject(message, LinkProperties.class);
+ callback.onAvailable(network, cap, lp, message.arg1 != 0);
+ break;
+ }
+ case CALLBACK_LOSING: {
+ callback.onLosing(network, message.arg1);
+ break;
+ }
+ case CALLBACK_LOST: {
+ callback.onLost(network);
+ break;
+ }
+ case CALLBACK_UNAVAIL: {
+ callback.onUnavailable();
+ break;
+ }
+ case CALLBACK_CAP_CHANGED: {
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+ callback.onCapabilitiesChanged(network, cap);
+ break;
+ }
+ case CALLBACK_IP_CHANGED: {
+ LinkProperties lp = getObject(message, LinkProperties.class);
+ callback.onLinkPropertiesChanged(network, lp);
+ break;
+ }
+ case CALLBACK_SUSPENDED: {
+ callback.onNetworkSuspended(network);
+ break;
+ }
+ case CALLBACK_RESUMED: {
+ callback.onNetworkResumed(network);
+ break;
+ }
+ case CALLBACK_BLK_CHANGED: {
+ boolean blocked = message.arg1 != 0;
+ callback.onBlockedStatusChanged(network, blocked);
+ }
+ }
+ }
+
+ private <T> T getObject(Message msg, Class<T> c) {
+ return (T) msg.getData().getParcelable(c.getSimpleName());
+ }
+ }
+
+ private CallbackHandler getDefaultHandler() {
+ synchronized (sCallbacks) {
+ if (sCallbackHandler == null) {
+ sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper());
+ }
+ return sCallbackHandler;
+ }
+ }
+
+ private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
+ private static CallbackHandler sCallbackHandler;
+
+ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
+ int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) {
+ printStackTrace();
+ checkCallbackNotNull(callback);
+ Preconditions.checkArgument(
+ reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities");
+ final NetworkRequest request;
+ final String callingPackageName = mContext.getOpPackageName();
+ try {
+ synchronized(sCallbacks) {
+ if (callback.networkRequest != null
+ && callback.networkRequest != ALREADY_UNREGISTERED) {
+ // TODO: throw exception instead and enforce 1:1 mapping of callbacks
+ // and requests (http://b/20701525).
+ Log.e(TAG, "NetworkCallback was already registered");
+ }
+ Messenger messenger = new Messenger(handler);
+ Binder binder = new Binder();
+ if (reqType == LISTEN) {
+ request = mService.listenForNetwork(
+ need, messenger, binder, callingPackageName);
+ } else {
+ request = mService.requestNetwork(
+ need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
+ callingPackageName, getAttributionTag());
+ }
+ if (request != null) {
+ sCallbacks.put(request, callback);
+ }
+ callback.networkRequest = request;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw convertServiceException(e);
+ }
+ return request;
+ }
+
+ /**
+ * Helper function to request a network with a particular legacy type.
+ *
+ * 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.
+ *
+ * @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,
+ 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);
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ *
+ * <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>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.
+ *
+ * <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}.
+ *
+ * <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
+ * {@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 satisfying a request with these capabilities.
+ *
+ * <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>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.
+ */
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * by a timeout.
+ *
+ * This function behaves identically to the non-timed-out version
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, 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.
+ *
+ * <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 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 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).
+ */
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, int timeoutMs) {
+ checkTimeout(timeoutMs);
+ 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 method behaves identically to
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} 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.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable} is called.
+ */
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
+ checkTimeout(timeoutMs);
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE, cbHandler);
+ }
+
+ /**
+ * The lookup key for a {@link Network} object included with the intent after
+ * successfully finding a network for the applications request. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * <p>
+ * Note that if you intend to invoke {@link Network#openConnection(java.net.URL)}
+ * then you must get a ConnectivityManager instance before doing so.
+ */
+ public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
+
+ /**
+ * The lookup key for a {@link NetworkRequest} object included with the intent after
+ * successfully finding a network for the applications request. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
+
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ *
+ * This function behaves identically to the version that takes a NetworkCallback, but instead
+ * of {@link NetworkCallback} a {@link PendingIntent} is used. This means
+ * the request may outlive the calling application and get called back when a suitable
+ * network is found.
+ * <p>
+ * The operation is an Intent broadcast that goes to a broadcast receiver that
+ * you registered with {@link Context#registerReceiver} or through the
+ * <receiver> tag in an AndroidManifest.xml file
+ * <p>
+ * The operation Intent is delivered with two extras, a {@link Network} typed
+ * extra called {@link #EXTRA_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_NETWORK_REQUEST} containing
+ * the original requests parameters. It is important to create a new,
+ * {@link NetworkCallback} based request before completing the processing of the
+ * Intent to reserve the network or it will be released shortly after the Intent
+ * is processed.
+ * <p>
+ * If there is already a request for this Intent registered (with the equality of
+ * two Intents defined by {@link Intent#filterEquals}), then it will be removed and
+ * replaced by this one, effectively releasing the previous {@link NetworkRequest}.
+ * <p>
+ * The request may be released normally by calling
+ * {@link #releaseNetworkRequest(android.app.PendingIntent)}.
+ * <p>It is presently unsupported to request a network with either
+ * {@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 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
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @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 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 PendingIntent operation) {
+ printStackTrace();
+ checkPendingIntentNotNull(operation);
+ try {
+ mService.pendingRequestForNetwork(
+ request.networkCapabilities, operation, mContext.getOpPackageName(),
+ getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw convertServiceException(e);
+ }
+ }
+
+ /**
+ * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
+ * <p>
+ * This method has the same behavior as
+ * {@link #unregisterNetworkCallback(android.app.PendingIntent)} with respect to
+ * releasing network resources and disconnecting.
+ *
+ * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
+ * PendingIntent passed to
+ * {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
+ * corresponding NetworkRequest you'd like to remove. Cannot be null.
+ */
+ public void releaseNetworkRequest(@NonNull PendingIntent operation) {
+ printStackTrace();
+ checkPendingIntentNotNull(operation);
+ try {
+ mService.releasePendingNetworkRequest(operation);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static void checkPendingIntentNotNull(PendingIntent intent) {
+ Preconditions.checkNotNull(intent, "PendingIntent cannot be null.");
+ }
+
+ private static void checkCallbackNotNull(NetworkCallback callback) {
+ Preconditions.checkNotNull(callback, "null NetworkCallback");
+ }
+
+ private static void checkTimeout(int timeoutMs) {
+ Preconditions.checkArgumentPositive(timeoutMs, "timeoutMs must be strictly positive.");
+ }
+
+ /**
+ * Registers to receive notifications about all networks which satisfy the given
+ * {@link NetworkRequest}. The callbacks 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 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,
+ @NonNull NetworkCallback networkCallback) {
+ registerNetworkCallback(request, networkCallback, getDefaultHandler());
+ }
+
+ /**
+ * Registers to receive notifications about all networks which satisfy the given
+ * {@link NetworkRequest}. The callbacks 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 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,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
+ }
+
+ /**
+ * Registers a PendingIntent to be sent when a network is available which satisfies the given
+ * {@link NetworkRequest}.
+ *
+ * This function behaves identically to the version that takes a NetworkCallback, but instead
+ * of {@link NetworkCallback} a {@link PendingIntent} is used. This means
+ * the request may outlive the calling application and get called back when a suitable
+ * network is found.
+ * <p>
+ * The operation is an Intent broadcast that goes to a broadcast receiver that
+ * you registered with {@link Context#registerReceiver} or through the
+ * <receiver> tag in an AndroidManifest.xml file
+ * <p>
+ * The operation Intent is delivered with two extras, a {@link Network} typed
+ * extra called {@link #EXTRA_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_NETWORK_REQUEST} containing
+ * the original requests parameters.
+ * <p>
+ * If there is already a request for this Intent registered (with the equality of
+ * two Intents defined by {@link Intent#filterEquals}), then it will be removed and
+ * replaced by this one, effectively releasing the previous {@link NetworkRequest}.
+ * <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,
+ @NonNull PendingIntent operation) {
+ printStackTrace();
+ checkPendingIntentNotNull(operation);
+ try {
+ mService.pendingListenForNetwork(
+ request.networkCapabilities, operation, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw convertServiceException(e);
+ }
+ }
+
+ /**
+ * Registers to receive notifications about changes in the system default network. The callbacks
+ * 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) {
+ registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
+ }
+
+ /**
+ * Registers to receive notifications about changes in the system default network. The callbacks
+ * 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,
+ @NonNull Handler handler) {
+ // This works because if the NetworkCapabilities are null,
+ // ConnectivityService takes them from the default request.
+ //
+ // Since the capabilities are exactly the same as the default request's
+ // capabilities, this request is guaranteed, at all times, to be
+ // satisfied by the same network, if any, that satisfies the default
+ // request, i.e., the system default network.
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0,
+ TRACK_DEFAULT, TYPE_NONE, cbHandler);
+ }
+
+ /**
+ * Requests bandwidth update for a given {@link Network} and returns whether the update request
+ * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
+ * network connection for updated bandwidth information. The caller will be notified via
+ * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this
+ * method assumes that the caller has previously called
+ * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} to listen for network
+ * changes.
+ *
+ * @param network {@link Network} specifying which network you're interested.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ */
+ public boolean requestBandwidthUpdate(@NonNull Network network) {
+ try {
+ return mService.requestBandwidthUpdate(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a {@code NetworkCallback} and possibly releases networks originating from
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and
+ * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} calls.
+ * If the given {@code NetworkCallback} had previously been used with
+ * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
+ * will be disconnected.
+ *
+ * Notifications that would have triggered that {@code NetworkCallback} will immediately stop
+ * triggering it as soon as this call returns.
+ *
+ * @param networkCallback The {@link NetworkCallback} used when making the request.
+ */
+ public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
+ printStackTrace();
+ checkCallbackNotNull(networkCallback);
+ final List<NetworkRequest> reqs = new ArrayList<>();
+ // Find all requests associated to this callback and stop callback triggers immediately.
+ // Callback is reusable immediately. http://b/20701525, http://b/35921499.
+ synchronized (sCallbacks) {
+ Preconditions.checkArgument(networkCallback.networkRequest != null,
+ "NetworkCallback was not registered");
+ if (networkCallback.networkRequest == ALREADY_UNREGISTERED) {
+ Log.d(TAG, "NetworkCallback was already unregistered");
+ return;
+ }
+ for (Map.Entry<NetworkRequest, NetworkCallback> e : sCallbacks.entrySet()) {
+ if (e.getValue() == networkCallback) {
+ reqs.add(e.getKey());
+ }
+ }
+ // TODO: throw exception if callback was registered more than once (http://b/20701525).
+ for (NetworkRequest r : reqs) {
+ try {
+ mService.releaseNetworkRequest(r);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ // Only remove mapping if rpc was successful.
+ sCallbacks.remove(r);
+ }
+ networkCallback.networkRequest = ALREADY_UNREGISTERED;
+ }
+ }
+
+ /**
+ * Unregisters a callback previously registered via
+ * {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
+ *
+ * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
+ * PendingIntent passed to
+ * {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
+ * Cannot be null.
+ */
+ public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
+ releaseNetworkRequest(operation);
+ }
+
+ /**
+ * Informs the system whether it should switch to {@code network} regardless of whether it is
+ * validated or not. If {@code accept} is true, and the network was explicitly selected by the
+ * user (e.g., by selecting a Wi-Fi network in the Settings app), then the network will become
+ * the system default network regardless of any other network that's currently connected. If
+ * {@code always} is true, then the choice is remembered, so that the next time the user
+ * connects to this network, the system will switch to it.
+ *
+ * @param network The network to accept.
+ * @param accept Whether to accept the network even if unvalidated.
+ * @param always Whether to remember this choice in the future.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ try {
+ mService.setAcceptUnvalidated(network, accept, always);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the system whether it should consider the network as validated even if it only has
+ * partial connectivity. If {@code accept} is true, then the network will be considered as
+ * validated even if connectivity is only partial. If {@code always} is true, then the choice
+ * is remembered, so that the next time the user connects to this network, the system will
+ * switch to it.
+ *
+ * @param network The network to accept.
+ * @param accept Whether to consider the network as validated even if it has partial
+ * connectivity.
+ * @param always Whether to remember this choice in the future.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+ public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
+ try {
+ mService.setAcceptPartialConnectivity(network, accept, always);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is
+ * only meaningful if the system is configured not to penalize such networks, e.g., if the
+ * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code
+ * NETWORK_AVOID_BAD_WIFI setting is unset}.
+ *
+ * @param network The network to accept.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setAvoidUnvalidated(Network network) {
+ try {
+ mService.setAvoidUnvalidated(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests that the system open the captive portal app on the specified network.
+ *
+ * @param network The network to log into.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void startCaptivePortalApp(Network network) {
+ try {
+ mService.startCaptivePortalApp(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests that the system open the captive portal app with the specified extras.
+ *
+ * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
+ * corresponding permission.
+ * @param network Network on which the captive portal was detected.
+ * @param appExtras Extras to include in the app start intent.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ public void startCaptivePortalApp(@NonNull Network network, @NonNull Bundle appExtras) {
+ try {
+ mService.startCaptivePortalAppInternal(network, appExtras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Determine whether the device is configured to avoid bad wifi.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
+ public boolean shouldAvoidBadWifi() {
+ try {
+ return mService.shouldAvoidBadWifi();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * It is acceptable to briefly use multipath data to provide seamless connectivity for
+ * time-sensitive user-facing operations when the system default network is temporarily
+ * unresponsive. The amount of data should be limited (less than one megabyte for every call to
+ * this method), and the operation should be infrequent to ensure that data usage is limited.
+ *
+ * An example of such an operation might be a time-sensitive foreground activity, such as a
+ * voice command, that the user is performing while walking out of range of a Wi-Fi network.
+ */
+ public static final int MULTIPATH_PREFERENCE_HANDOVER = 1 << 0;
+
+ /**
+ * It is acceptable to use small amounts of multipath data on an ongoing basis to provide
+ * a backup channel for traffic that is primarily going over another network.
+ *
+ * An example might be maintaining backup connections to peers or servers for the purpose of
+ * fast fallback if the default network is temporarily unresponsive or disconnects. The traffic
+ * on backup paths should be negligible compared to the traffic on the main path.
+ */
+ public static final int MULTIPATH_PREFERENCE_RELIABILITY = 1 << 1;
+
+ /**
+ * It is acceptable to use metered data to improve network latency and performance.
+ */
+ public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 1 << 2;
+
+ /**
+ * Return value to use for unmetered networks. On such networks we currently set all the flags
+ * to true.
+ * @hide
+ */
+ public static final int MULTIPATH_PREFERENCE_UNMETERED =
+ MULTIPATH_PREFERENCE_HANDOVER |
+ MULTIPATH_PREFERENCE_RELIABILITY |
+ MULTIPATH_PREFERENCE_PERFORMANCE;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ MULTIPATH_PREFERENCE_HANDOVER,
+ MULTIPATH_PREFERENCE_RELIABILITY,
+ MULTIPATH_PREFERENCE_PERFORMANCE,
+ })
+ public @interface MultipathPreference {
+ }
+
+ /**
+ * Provides a hint to the calling application on whether it is desirable to use the
+ * multinetwork APIs (e.g., {@link Network#openConnection}, {@link Network#bindSocket}, etc.)
+ * for multipath data transfer on this network when it is not the system default network.
+ * Applications desiring to use multipath network protocols should call this method before
+ * each such operation.
+ *
+ * @param network The network on which the application desires to use multipath data.
+ * If {@code null}, this method will return the a preference that will generally
+ * apply to metered networks.
+ * @return a bitwise OR of zero or more of the {@code MULTIPATH_PREFERENCE_*} constants.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
+ try {
+ return mService.getMultipathPreference(network);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ /**
+ * Binds the current process to {@code network}. All Sockets created in the future
+ * (and not explicitly bound via a bound SocketFactory from
+ * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
+ * {@code network}. All host name resolutions will be limited to {@code network} as well.
+ * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
+ * work and all host name resolutions will fail. This is by design so an application doesn't
+ * accidentally use Sockets it thinks are still bound to a particular {@link Network}.
+ * To clear binding pass {@code null} for {@code network}. Using individually bound
+ * Sockets created by Network.getSocketFactory().createSocket() and
+ * performing network-specific host name resolutions via
+ * {@link Network#getAllByName Network.getAllByName} is preferred to calling
+ * {@code bindProcessToNetwork}.
+ *
+ * @param network The {@link Network} to bind the current process to, or {@code null} to clear
+ * the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ */
+ public boolean bindProcessToNetwork(@Nullable Network network) {
+ // Forcing callers to call through non-static function ensures ConnectivityManager
+ // instantiated.
+ return setProcessDefaultNetwork(network);
+ }
+
+ /**
+ * Binds the current process to {@code network}. All Sockets created in the future
+ * (and not explicitly bound via a bound SocketFactory from
+ * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
+ * {@code network}. All host name resolutions will be limited to {@code network} as well.
+ * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
+ * work and all host name resolutions will fail. This is by design so an application doesn't
+ * accidentally use Sockets it thinks are still bound to a particular {@link Network}.
+ * To clear binding pass {@code null} for {@code network}. Using individually bound
+ * Sockets created by Network.getSocketFactory().createSocket() and
+ * performing network-specific host name resolutions via
+ * {@link Network#getAllByName Network.getAllByName} is preferred to calling
+ * {@code setProcessDefaultNetwork}.
+ *
+ * @param network The {@link Network} to bind the current process to, or {@code null} to clear
+ * the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ * @deprecated This function can throw {@link IllegalStateException}. Use
+ * {@link #bindProcessToNetwork} instead. {@code bindProcessToNetwork}
+ * is a direct replacement.
+ */
+ @Deprecated
+ public static boolean setProcessDefaultNetwork(@Nullable Network network) {
+ int netId = (network == null) ? NETID_UNSET : network.netId;
+ boolean isSameNetId = (netId == NetworkUtils.getBoundNetworkForProcess());
+
+ if (netId != NETID_UNSET) {
+ netId = network.getNetIdForResolv();
+ }
+
+ if (!NetworkUtils.bindProcessToNetwork(netId)) {
+ return false;
+ }
+
+ if (!isSameNetId) {
+ // Set HTTP proxy system properties to match network.
+ // TODO: Deprecate this static method and replace it with a non-static version.
+ try {
+ Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ } catch (SecurityException e) {
+ // The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
+ Log.e(TAG, "Can't set proxy properties", e);
+ }
+ // Must flush DNS cache as new network may have different DNS resolutions.
+ InetAddress.clearDnsCache();
+ // Must flush socket pool as idle sockets will be bound to previous network and may
+ // cause subsequent fetches to be performed on old network.
+ NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the {@link Network} currently bound to this process via
+ * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ *
+ * @return {@code Network} to which this process is bound, or {@code null}.
+ */
+ @Nullable
+ public Network getBoundNetworkForProcess() {
+ // Forcing callers to call thru non-static function ensures ConnectivityManager
+ // instantiated.
+ return getProcessDefaultNetwork();
+ }
+
+ /**
+ * Returns the {@link Network} currently bound to this process via
+ * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ *
+ * @return {@code Network} to which this process is bound, or {@code null}.
+ * @deprecated Using this function can lead to other functions throwing
+ * {@link IllegalStateException}. Use {@link #getBoundNetworkForProcess} instead.
+ * {@code getBoundNetworkForProcess} is a direct replacement.
+ */
+ @Deprecated
+ @Nullable
+ public static Network getProcessDefaultNetwork() {
+ int netId = NetworkUtils.getBoundNetworkForProcess();
+ if (netId == NETID_UNSET) return null;
+ return new Network(netId);
+ }
+
+ private void unsupportedStartingFrom(int version) {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ // The getApplicationInfo() call we make below is not supported in system context. Let
+ // the call through here, and rely on the fact that ConnectivityService will refuse to
+ // allow the system to use these APIs anyway.
+ return;
+ }
+
+ if (mContext.getApplicationInfo().targetSdkVersion >= version) {
+ throw new UnsupportedOperationException(
+ "This method is not supported in target SDK version " + version + " and above");
+ }
+ }
+
+ // Checks whether the calling app can use the legacy routing API (startUsingNetworkFeature,
+ // stopUsingNetworkFeature, requestRouteToHost), and if not throw UnsupportedOperationException.
+ // TODO: convert the existing system users (Tethering, GnssLocationProvider) to the new APIs and
+ // remove these exemptions. Note that this check is not secure, and apps can still access these
+ // functions by accessing ConnectivityService directly. However, it should be clear that doing
+ // so is unsupported and may break in the future. http://b/22728205
+ private void checkLegacyRoutingApiAccess() {
+ unsupportedStartingFrom(VERSION_CODES.M);
+ }
+
+ /**
+ * Binds host resolutions performed by this process to {@code network}.
+ * {@link #bindProcessToNetwork} takes precedence over this setting.
+ *
+ * @param network The {@link Network} to bind host resolutions from the current process to, or
+ * {@code null} to clear the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ * @hide
+ * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}.
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static boolean setProcessDefaultNetworkForHostResolution(Network network) {
+ return NetworkUtils.bindProcessToNetworkForHostResolution(
+ (network == null) ? NETID_UNSET : network.getNetIdForResolv());
+ }
+
+ /**
+ * Device is not restricting metered network activity while application is running on
+ * background.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1;
+
+ /**
+ * Device is restricting metered network activity while application is running on background,
+ * but application is allowed to bypass it.
+ * <p>
+ * In this state, application should take action to mitigate metered network access.
+ * For example, a music streaming application should switch to a low-bandwidth bitrate.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2;
+
+ /**
+ * Device is restricting metered network activity while application is running on background.
+ * <p>
+ * In this state, application should not try to use the network while running on background,
+ * because it would be denied.
+ */
+ public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3;
+
+ /**
+ * A change in the background metered network activity restriction has occurred.
+ * <p>
+ * Applications should call {@link #getRestrictBackgroundStatus()} to check if the restriction
+ * applies to them.
+ * <p>
+ * This is only sent to registered receivers, not manifest receivers.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_RESTRICT_BACKGROUND_CHANGED =
+ "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ RESTRICT_BACKGROUND_STATUS_DISABLED,
+ RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {
+ }
+
+ private INetworkPolicyManager getNetworkPolicyManager() {
+ synchronized (this) {
+ if (mNPManager != null) {
+ return mNPManager;
+ }
+ mNPManager = INetworkPolicyManager.Stub.asInterface(ServiceManager
+ .getService(Context.NETWORK_POLICY_SERVICE));
+ return mNPManager;
+ }
+ }
+
+ /**
+ * Determines if the calling application is subject to metered network restrictions while
+ * running on background.
+ *
+ * @return {@link #RESTRICT_BACKGROUND_STATUS_DISABLED},
+ * {@link #RESTRICT_BACKGROUND_STATUS_ENABLED},
+ * or {@link #RESTRICT_BACKGROUND_STATUS_WHITELISTED}
+ */
+ public @RestrictBackgroundStatus int getRestrictBackgroundStatus() {
+ try {
+ return getNetworkPolicyManager().getRestrictBackgroundByCaller();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The network watchlist is a list of domains and IP addresses that are associated with
+ * potentially harmful apps. This method returns the SHA-256 of the watchlist config file
+ * currently used by the system for validation purposes.
+ *
+ * @return Hash of network watchlist config file. Null if config does not exist.
+ */
+ @Nullable
+ public byte[] getNetworkWatchlistConfigHash() {
+ try {
+ return mService.getNetworkWatchlistConfigHash();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get watchlist config hash");
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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 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 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) {
+ ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
+ try {
+ return mService.getConnectionOwnerUid(connectionInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void printStackTrace() {
+ if (DEBUG) {
+ final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+ final StringBuffer sb = new StringBuffer();
+ for (int i = 3; i < callStack.length; i++) {
+ final String stackTrace = callStack[i].toString();
+ if (stackTrace == null || stackTrace.contains("android.os")) {
+ break;
+ }
+ sb.append(" [").append(stackTrace).append("]");
+ }
+ Log.d(TAG, "StackLog:" + sb.toString());
+ }
+ }
+
+ /**
+ * Simulates a Data Stall for the specified Network.
+ *
+ * <p>This method should only be used for tests.
+ *
+ * <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
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @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();
+ }
+ }
+
+ private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
+ Log.d(TAG, "setOemNetworkPreference called with preference: "
+ + preference.toString());
+ }
+
+ @NonNull
+ private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>();
+
+ /**
+ * Registers a {@link QosSocketInfo} with an associated {@link QosCallback}. The callback will
+ * receive available QoS events related to the {@link Network} and local ip + port
+ * specified within socketInfo.
+ * <p/>
+ * The same {@link QosCallback} must be unregistered before being registered a second time,
+ * otherwise {@link QosCallbackRegistrationException} is thrown.
+ * <p/>
+ * This API does not, in itself, require any permission if called with a network that is not
+ * restricted. However, the underlying implementation currently only supports the IMS network,
+ * which is always restricted. That means non-preinstalled callers can't possibly find this API
+ * useful, because they'd never be called back on networks that they would have access to.
+ *
+ * @throws SecurityException if {@link QosSocketInfo#getNetwork()} is restricted and the app is
+ * missing CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+ * @throws QosCallback.QosCallbackRegistrationException if qosCallback is already registered.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ *
+ * Exceptions after the time of registration is passed through
+ * {@link QosCallback#onError(QosCallbackException)}. see: {@link QosCallbackException}.
+ *
+ * @param socketInfo the socket information used to match QoS events
+ * @param callback receives qos events that satisfy socketInfo
+ * @param executor The executor on which the callback will be invoked. The provided
+ * {@link Executor} must run callback sequentially, otherwise the order of
+ * callbacks cannot be guaranteed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
+ @NonNull final QosCallback callback,
+ @CallbackExecutor @NonNull final Executor executor) {
+ Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
+ Objects.requireNonNull(callback, "callback must be non-null");
+ Objects.requireNonNull(executor, "executor must be non-null");
+
+ try {
+ synchronized (mQosCallbackConnections) {
+ if (getQosCallbackConnection(callback) == null) {
+ final QosCallbackConnection connection =
+ new QosCallbackConnection(this, callback, executor);
+ mQosCallbackConnections.add(connection);
+ mService.registerQosSocketCallback(socketInfo, connection);
+ } else {
+ Log.e(TAG, "registerQosCallback: Callback already registered");
+ throw new QosCallbackRegistrationException();
+ }
+ }
+ } catch (final RemoteException e) {
+ Log.e(TAG, "registerQosCallback: Error while registering ", e);
+
+ // The same unregister method method is called for consistency even though nothing
+ // will be sent to the ConnectivityService since the callback was never successfully
+ // registered.
+ unregisterQosCallback(callback);
+ e.rethrowFromSystemServer();
+ } catch (final ServiceSpecificException e) {
+ Log.e(TAG, "registerQosCallback: Error while registering ", e);
+ unregisterQosCallback(callback);
+ throw convertServiceException(e);
+ }
+ }
+
+ /**
+ * Unregisters the given {@link QosCallback}. The {@link QosCallback} will no longer receive
+ * events once unregistered and can be registered a second time.
+ * <p/>
+ * If the {@link QosCallback} does not have an active registration, it is a no-op.
+ *
+ * @param callback the callback being unregistered
+ *
+ * @hide
+ */
+ @SystemApi
+ public void unregisterQosCallback(@NonNull final QosCallback callback) {
+ Objects.requireNonNull(callback, "The callback must be non-null");
+ try {
+ synchronized (mQosCallbackConnections) {
+ final QosCallbackConnection connection = getQosCallbackConnection(callback);
+ if (connection != null) {
+ connection.stopReceivingMessages();
+ mService.unregisterQosCallback(connection);
+ mQosCallbackConnections.remove(connection);
+ } else {
+ Log.d(TAG, "unregisterQosCallback: Callback not registered");
+ }
+ }
+ } catch (final RemoteException e) {
+ Log.e(TAG, "unregisterQosCallback: Error while unregistering ", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the connection related to the callback.
+ *
+ * @param callback the callback to look up
+ * @return the related connection
+ */
+ @Nullable
+ private QosCallbackConnection getQosCallbackConnection(final QosCallback callback) {
+ for (final QosCallbackConnection connection : mQosCallbackConnections) {
+ // Checking by reference here is intentional
+ if (connection.getCallback() == callback) {
+ return connection;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+ * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
+ * be used to request that the system provide a network without causing the network to be
+ * in the foreground.
+ *
+ * <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>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.
+ *
+ * <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}.
+ *
+ * <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.
+ *
+ * <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>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 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(NetworkCallback)}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * If null, the callback is invoked on the default internal Handler.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @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.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void requestBackgroundNetwork(@NonNull NetworkRequest request,
+ @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+ final NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
+ TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+ }
+}
diff --git a/framework/src/android/net/ConnectivityMetricsEvent.aidl b/framework/src/android/net/ConnectivityMetricsEvent.aidl
new file mode 100644
index 0000000..1c541dc
--- /dev/null
+++ b/framework/src/android/net/ConnectivityMetricsEvent.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 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;
+
+/** {@hide} */
+parcelable ConnectivityMetricsEvent;
diff --git a/framework/src/android/net/ConnectivityThread.java b/framework/src/android/net/ConnectivityThread.java
new file mode 100644
index 0000000..0b218e7
--- /dev/null
+++ b/framework/src/android/net/ConnectivityThread.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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.HandlerThread;
+import android.os.Looper;
+
+/**
+ * Shared singleton connectivity thread for the system. This is a thread for
+ * connectivity operations such as AsyncChannel connections to system services.
+ * Various connectivity manager objects can use this singleton as a common
+ * resource for their handlers instead of creating separate threads of their own.
+ * @hide
+ */
+public final class ConnectivityThread extends HandlerThread {
+
+ // A class implementing the lazy holder idiom: the unique static instance
+ // of ConnectivityThread is instantiated in a thread-safe way (guaranteed by
+ // the language specs) the first time that Singleton is referenced in get()
+ // or getInstanceLooper().
+ private static class Singleton {
+ private static final ConnectivityThread INSTANCE = createInstance();
+ }
+
+ private ConnectivityThread() {
+ super("ConnectivityThread");
+ }
+
+ private static ConnectivityThread createInstance() {
+ ConnectivityThread t = new ConnectivityThread();
+ t.start();
+ return t;
+ }
+
+ public static ConnectivityThread get() {
+ return Singleton.INSTANCE;
+ }
+
+ public static Looper getInstanceLooper() {
+ return Singleton.INSTANCE.getLooper();
+ }
+}
diff --git a/framework/src/android/net/DhcpInfo.aidl b/framework/src/android/net/DhcpInfo.aidl
new file mode 100644
index 0000000..29cd21f
--- /dev/null
+++ b/framework/src/android/net/DhcpInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, 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;
+
+parcelable DhcpInfo;
diff --git a/framework/src/android/net/DhcpInfo.java b/framework/src/android/net/DhcpInfo.java
new file mode 100644
index 0000000..912df67
--- /dev/null
+++ b/framework/src/android/net/DhcpInfo.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 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 simple object for retrieving the results of a DHCP request.
+ */
+public class DhcpInfo implements Parcelable {
+ public int ipAddress;
+ public int gateway;
+ public int netmask;
+ public int dns1;
+ public int dns2;
+ public int serverAddress;
+
+ public int leaseDuration;
+
+ public DhcpInfo() {
+ super();
+ }
+
+ /** copy constructor {@hide} */
+ public DhcpInfo(DhcpInfo source) {
+ if (source != null) {
+ ipAddress = source.ipAddress;
+ gateway = source.gateway;
+ netmask = source.netmask;
+ dns1 = source.dns1;
+ dns2 = source.dns2;
+ serverAddress = source.serverAddress;
+ leaseDuration = source.leaseDuration;
+ }
+ }
+
+ public String toString() {
+ StringBuffer str = new StringBuffer();
+
+ str.append("ipaddr "); putAddress(str, ipAddress);
+ str.append(" gateway "); putAddress(str, gateway);
+ str.append(" netmask "); putAddress(str, netmask);
+ str.append(" dns1 "); putAddress(str, dns1);
+ str.append(" dns2 "); putAddress(str, dns2);
+ str.append(" DHCP server "); putAddress(str, serverAddress);
+ str.append(" lease ").append(leaseDuration).append(" seconds");
+
+ return str.toString();
+ }
+
+ private static void putAddress(StringBuffer buf, int addr) {
+ buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress());
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(ipAddress);
+ dest.writeInt(gateway);
+ dest.writeInt(netmask);
+ dest.writeInt(dns1);
+ dest.writeInt(dns2);
+ dest.writeInt(serverAddress);
+ dest.writeInt(leaseDuration);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @android.annotation.NonNull Creator<DhcpInfo> CREATOR =
+ new Creator<DhcpInfo>() {
+ public DhcpInfo createFromParcel(Parcel in) {
+ DhcpInfo info = new DhcpInfo();
+ info.ipAddress = in.readInt();
+ info.gateway = in.readInt();
+ info.netmask = in.readInt();
+ info.dns1 = in.readInt();
+ info.dns2 = in.readInt();
+ info.serverAddress = in.readInt();
+ info.leaseDuration = in.readInt();
+ return info;
+ }
+
+ public DhcpInfo[] newArray(int size) {
+ return new DhcpInfo[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
new file mode 100644
index 0000000..3f7660f
--- /dev/null
+++ b/framework/src/android/net/DnsResolver.java
@@ -0,0 +1,577 @@
+/*
+ * 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 android.net.NetworkUtils.getDnsNetwork;
+import static android.net.NetworkUtils.resNetworkCancel;
+import static android.net.NetworkUtils.resNetworkQuery;
+import static android.net.NetworkUtils.resNetworkResult;
+import static android.net.NetworkUtils.resNetworkSend;
+import static android.net.util.DnsUtils.haveIpv4;
+import static android.net.util.DnsUtils.haveIpv6;
+import static android.net.util.DnsUtils.rfc6724Sort;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.system.OsConstants.ENONET;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.os.Looper;
+import android.os.MessageQueue;
+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;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Dns resolver class for asynchronous dns querying
+ *
+ * Note that if a client sends a query with more than 1 record in the question section but
+ * the remote dns server does not support this, it may not respond at all, leading to a timeout.
+ *
+ */
+public final class DnsResolver {
+ private static final String TAG = "DnsResolver";
+ private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int MAXPACKET = 8 * 1024;
+ private static final int SLEEP_TIME_MS = 2;
+
+ @IntDef(prefix = { "CLASS_" }, value = {
+ CLASS_IN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryClass {}
+ public static final int CLASS_IN = 1;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_A,
+ TYPE_AAAA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryType {}
+ public static final int TYPE_A = 1;
+ public static final int TYPE_AAAA = 28;
+
+ @IntDef(prefix = { "FLAG_" }, value = {
+ FLAG_EMPTY,
+ FLAG_NO_RETRY,
+ FLAG_NO_CACHE_STORE,
+ FLAG_NO_CACHE_LOOKUP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryFlag {}
+ public static final int FLAG_EMPTY = 0;
+ public static final int FLAG_NO_RETRY = 1 << 0;
+ public static final int FLAG_NO_CACHE_STORE = 1 << 1;
+ public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
+
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_PARSE,
+ ERROR_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @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 {@link ParseException}.
+ */
+ public static final int ERROR_PARSE = 0;
+ /**
+ * Indicates that there was an error sending the query.
+ * The cause of this error is available via getCause() and is an ErrnoException.
+ */
+ public static final int ERROR_SYSTEM = 1;
+
+ private static final int NETID_UNSET = 0;
+
+ private static final DnsResolver sInstance = new DnsResolver();
+
+ /**
+ * Get instance for DnsResolver
+ */
+ public static @NonNull DnsResolver getInstance() {
+ return sInstance;
+ }
+
+ private DnsResolver() {}
+
+ /**
+ * Base interface for answer callbacks
+ *
+ * @param <T> The type of the answer
+ */
+ public interface Callback<T> {
+ /**
+ * Success response to
+ * {@link android.net.DnsResolver#query query()} or
+ * {@link android.net.DnsResolver#rawQuery rawQuery()}.
+ *
+ * Invoked when the answer to a query was successfully parsed.
+ *
+ * @param answer <T> answer to the query.
+ * @param rcode The response code in the DNS response.
+ *
+ * {@see android.net.DnsResolver#query query()}
+ */
+ void onAnswer(@NonNull T answer, int rcode);
+ /**
+ * Error response to
+ * {@link android.net.DnsResolver#query query()} or
+ * {@link android.net.DnsResolver#rawQuery rawQuery()}.
+ *
+ * Invoked when there is no valid answer to
+ * {@link android.net.DnsResolver#query query()}
+ * {@link android.net.DnsResolver#rawQuery rawQuery()}.
+ *
+ * @param error a {@link DnsException} object with additional
+ * detail regarding the failure
+ */
+ void onError(@NonNull DnsException error);
+ }
+
+ /**
+ * Class to represent DNS error
+ */
+ public static class DnsException extends Exception {
+ /**
+ * DNS error code as one of the ERROR_* constants
+ */
+ @DnsError public final int code;
+
+ DnsException(@DnsError int code, @Nullable Throwable cause) {
+ super(cause);
+ this.code = code;
+ }
+ }
+
+ /**
+ * Send a raw DNS query.
+ * The answer will be provided asynchronously through the provided {@link Callback}.
+ *
+ * @param network {@link Network} specifying which network to query on.
+ * {@code null} for query on default network.
+ * @param query blob message to query
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param executor The {@link Executor} that the callback should be executed on.
+ * @param cancellationSignal used by the caller to signal if the query should be
+ * cancelled. May be {@code null}.
+ * @param callback a {@link Callback} which will be called to notify the caller
+ * of the result of dns query.
+ */
+ public void rawQuery(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Callback<? super byte[]> callback) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ final Object lock = new Object();
+ final FileDescriptor queryfd;
+ try {
+ queryfd = resNetworkSend((network != null)
+ ? network.getNetIdForResolv() : NETID_UNSET, query, query.length, flags);
+ } catch (ErrnoException e) {
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
+ }
+
+ /**
+ * Send a DNS query with the specified name, class and query type.
+ * The answer will be provided asynchronously through the provided {@link Callback}.
+ *
+ * @param network {@link Network} specifying which network to query on.
+ * {@code null} for query on default network.
+ * @param domain domain name to query
+ * @param nsClass dns class as one of the CLASS_* constants
+ * @param nsType dns resource record (RR) type as one of the TYPE_* constants
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param executor The {@link Executor} that the callback should be executed on.
+ * @param cancellationSignal used by the caller to signal if the query should be
+ * cancelled. May be {@code null}.
+ * @param callback a {@link Callback} which will be called to notify the caller
+ * of the result of dns query.
+ */
+ public void rawQuery(@Nullable Network network, @NonNull String domain,
+ @QueryClass int nsClass, @QueryType int nsType, @QueryFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Callback<? super byte[]> callback) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ final Object lock = new Object();
+ final FileDescriptor queryfd;
+ try {
+ queryfd = resNetworkQuery((network != null)
+ ? network.getNetIdForResolv() : NETID_UNSET, domain, nsClass, nsType, flags);
+ } catch (ErrnoException e) {
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
+ }
+
+ private class InetAddressAnswerAccumulator implements Callback<byte[]> {
+ private final List<InetAddress> mAllAnswers;
+ private final Network mNetwork;
+ private int mRcode;
+ private DnsException mDnsException;
+ private final Callback<? super List<InetAddress>> mUserCallback;
+ private final int mTargetAnswerCount;
+ private int mReceivedAnswerCount = 0;
+
+ InetAddressAnswerAccumulator(@NonNull Network network, int size,
+ @NonNull Callback<? super List<InetAddress>> callback) {
+ mNetwork = network;
+ mTargetAnswerCount = size;
+ mAllAnswers = new ArrayList<>();
+ mUserCallback = callback;
+ }
+
+ private boolean maybeReportError() {
+ if (mRcode != 0) {
+ mUserCallback.onAnswer(mAllAnswers, mRcode);
+ return true;
+ }
+ if (mDnsException != null) {
+ mUserCallback.onError(mDnsException);
+ return true;
+ }
+ return false;
+ }
+
+ private void maybeReportAnswer() {
+ if (++mReceivedAnswerCount != mTargetAnswerCount) return;
+ if (mAllAnswers.isEmpty() && maybeReportError()) return;
+ mUserCallback.onAnswer(rfc6724Sort(mNetwork, mAllAnswers), mRcode);
+ }
+
+ @Override
+ public void onAnswer(@NonNull byte[] answer, int rcode) {
+ // If at least one query succeeded, return an rcode of 0.
+ // Otherwise, arbitrarily return the first rcode received.
+ if (mReceivedAnswerCount == 0 || rcode == 0) {
+ mRcode = rcode;
+ }
+ try {
+ mAllAnswers.addAll(new DnsAddressAnswer(answer).getAddresses());
+ } 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();
+ }
+
+ @Override
+ public void onError(@NonNull DnsException error) {
+ mDnsException = error;
+ maybeReportAnswer();
+ }
+ }
+
+ /**
+ * Send a DNS query with the specified name on a network with both IPv4 and IPv6,
+ * get back a set of InetAddresses with rfc6724 sorting style asynchronously.
+ *
+ * This method will examine the connection ability on given network, and query IPv4
+ * and IPv6 if connection is available.
+ *
+ * If at least one query succeeded with valid answer, rcode will be 0
+ *
+ * The answer will be provided asynchronously through the provided {@link Callback}.
+ *
+ * @param network {@link Network} specifying which network to query on.
+ * {@code null} for query on default network.
+ * @param domain domain name to query
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param executor The {@link Executor} that the callback should be executed on.
+ * @param cancellationSignal used by the caller to signal if the query should be
+ * cancelled. May be {@code null}.
+ * @param callback a {@link Callback} which will be called to notify the
+ * caller of the result of dns query.
+ */
+ public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Callback<? super List<InetAddress>> callback) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ final Object lock = new Object();
+ final Network queryNetwork;
+ try {
+ queryNetwork = (network != null) ? network : getDnsNetwork();
+ } catch (ErrnoException e) {
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+ final boolean queryIpv6 = haveIpv6(queryNetwork);
+ final boolean queryIpv4 = haveIpv4(queryNetwork);
+
+ // This can only happen if queryIpv4 and queryIpv6 are both false.
+ // This almost certainly means that queryNetwork does not exist or no longer exists.
+ if (!queryIpv6 && !queryIpv4) {
+ executor.execute(() -> callback.onError(
+ new DnsException(ERROR_SYSTEM, new ErrnoException("resNetworkQuery", ENONET))));
+ return;
+ }
+
+ final FileDescriptor v4fd;
+ final FileDescriptor v6fd;
+
+ int queryCount = 0;
+
+ if (queryIpv6) {
+ try {
+ v6fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN,
+ TYPE_AAAA, flags);
+ } catch (ErrnoException e) {
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+ queryCount++;
+ } else v6fd = null;
+
+ // Avoiding gateways drop packets if queries are sent too close together
+ try {
+ Thread.sleep(SLEEP_TIME_MS);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+
+ if (queryIpv4) {
+ try {
+ v4fd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, TYPE_A,
+ flags);
+ } catch (ErrnoException e) {
+ if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+ queryCount++;
+ } else v4fd = null;
+
+ final InetAddressAnswerAccumulator accumulator =
+ new InetAddressAnswerAccumulator(queryNetwork, queryCount, callback);
+
+ synchronized (lock) {
+ if (queryIpv6) {
+ registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
+ }
+ if (queryIpv4) {
+ registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
+ }
+ if (cancellationSignal == null) return;
+ cancellationSignal.setOnCancelListener(() -> {
+ synchronized (lock) {
+ if (queryIpv4) cancelQuery(v4fd);
+ if (queryIpv6) cancelQuery(v6fd);
+ }
+ });
+ }
+ }
+
+ /**
+ * Send a DNS query with the specified name and query type, get back a set of
+ * InetAddresses with rfc6724 sorting style asynchronously.
+ *
+ * The answer will be provided asynchronously through the provided {@link Callback}.
+ *
+ * @param network {@link Network} specifying which network to query on.
+ * {@code null} for query on default network.
+ * @param domain domain name to query
+ * @param nsType dns resource record (RR) type as one of the TYPE_* constants
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param executor The {@link Executor} that the callback should be executed on.
+ * @param cancellationSignal used by the caller to signal if the query should be
+ * cancelled. May be {@code null}.
+ * @param callback a {@link Callback} which will be called to notify the caller
+ * of the result of dns query.
+ */
+ public void query(@Nullable Network network, @NonNull String domain,
+ @QueryType int nsType, @QueryFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull Callback<? super List<InetAddress>> callback) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ final Object lock = new Object();
+ final FileDescriptor queryfd;
+ final Network queryNetwork;
+ try {
+ queryNetwork = (network != null) ? network : getDnsNetwork();
+ queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType,
+ flags);
+ } catch (ErrnoException e) {
+ executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e)));
+ return;
+ }
+ final InetAddressAnswerAccumulator accumulator =
+ new InetAddressAnswerAccumulator(queryNetwork, 1, callback);
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, accumulator, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
+ }
+
+ /**
+ * Class to retrieve DNS response
+ *
+ * @hide
+ */
+ public static final class DnsResponse {
+ public final @NonNull byte[] answerbuf;
+ public final int rcode;
+ public DnsResponse(@NonNull byte[] answerbuf, int rcode) {
+ this.answerbuf = answerbuf;
+ this.rcode = rcode;
+ }
+ }
+
+ private void registerFDListener(@NonNull Executor executor,
+ @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
+ @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
+ final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
+ mainThreadMessageQueue.addOnFileDescriptorEventListener(
+ queryfd,
+ FD_EVENTS,
+ (fd, events) -> {
+ // b/134310704
+ // Unregister fd event listener before resNetworkResult is called to prevent
+ // race condition caused by fd reused.
+ // For example when querying v4 and v6, it's possible that the first query ends
+ // and the fd is closed before the second request starts, which might return
+ // the same fd for the second request. By that time, the looper must have
+ // unregistered the fd, otherwise another event listener can't be registered.
+ mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
+
+ executor.execute(() -> {
+ DnsResponse resp = null;
+ ErrnoException exception = null;
+ synchronized (lock) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ try {
+ resp = resNetworkResult(fd); // Closes fd, marks it invalid.
+ } catch (ErrnoException e) {
+ Log.e(TAG, "resNetworkResult:" + e.toString());
+ exception = e;
+ }
+ }
+ if (exception != null) {
+ answerCallback.onError(new DnsException(ERROR_SYSTEM, exception));
+ return;
+ }
+ answerCallback.onAnswer(resp.answerbuf, resp.rcode);
+ });
+
+ // The file descriptor has already been unregistered, so it does not really
+ // matter what is returned here. In spirit 0 (meaning "unregister this FD")
+ // is still the closest to what the looper needs to do. When returning 0,
+ // Looper knows to ignore the fd if it has already been unregistered.
+ return 0;
+ });
+ }
+
+ private void cancelQuery(@NonNull FileDescriptor queryfd) {
+ if (!queryfd.valid()) return;
+ Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
+ resNetworkCancel(queryfd); // Closes fd, marks it invalid.
+ }
+
+ private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
+ @NonNull FileDescriptor queryfd, @NonNull Object lock) {
+ cancellationSignal.setOnCancelListener(() -> {
+ synchronized (lock) {
+ cancelQuery(queryfd);
+ }
+ });
+ }
+
+ private static class DnsAddressAnswer extends DnsPacket {
+ private static final String TAG = "DnsResolver.DnsAddressAnswer";
+ private static final boolean DBG = false;
+
+ private final int mQueryType;
+
+ DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
+ super(data);
+ if ((mHeader.flags & (1 << 15)) == 0) {
+ throw new ParseException("Not an answer packet");
+ }
+ if (mHeader.getRecordCount(QDSECTION) == 0) {
+ throw new ParseException("No question found");
+ }
+ // Expect only one question in question section.
+ mQueryType = mRecords[QDSECTION].get(0).nsType;
+ }
+
+ public @NonNull List<InetAddress> getAddresses() {
+ final List<InetAddress> results = new ArrayList<InetAddress>();
+ if (mHeader.getRecordCount(ANSECTION) == 0) return results;
+
+ for (final DnsRecord ansSec : mRecords[ANSECTION]) {
+ // Only support A and AAAA, also ignore answers if query type != answer type.
+ int nsType = ansSec.nsType;
+ if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
+ continue;
+ }
+ try {
+ results.add(InetAddress.getByAddress(ansSec.getRR()));
+ } catch (UnknownHostException e) {
+ if (DBG) {
+ Log.w(TAG, "rr to address fail");
+ }
+ }
+ }
+ return results;
+ }
+ }
+
+}
diff --git a/framework/src/android/net/ICaptivePortal.aidl b/framework/src/android/net/ICaptivePortal.aidl
new file mode 100644
index 0000000..fe21905
--- /dev/null
+++ b/framework/src/android/net/ICaptivePortal.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2015, 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;
+
+/**
+ * Interface to inform NetworkMonitor of decisions of app handling captive portal.
+ * @hide
+ */
+oneway interface ICaptivePortal {
+ void appRequest(int request);
+ void appResponse(int response);
+ void logEvent(int eventId, String packageName);
+}
diff --git a/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl b/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
new file mode 100644
index 0000000..82b64a9
--- /dev/null
+++ b/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -0,0 +1,28 @@
+/**
+ *
+ * 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.net.ConnectivityDiagnosticsManager;
+import android.net.Network;
+
+/** @hide */
+oneway interface IConnectivityDiagnosticsCallback {
+ void onConnectivityReportAvailable(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+ void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
+ void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
+}
\ No newline at end of file
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
new file mode 100644
index 0000000..1b4d2e4
--- /dev/null
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -0,0 +1,246 @@
+/**
+ * Copyright (c) 2008, 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.PendingIntent;
+import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.IConnectivityDiagnosticsCallback;
+import android.net.IQosCallback;
+import android.net.ISocketKeepaliveCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgentConfig;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.NetworkState;
+import android.net.ProxyInfo;
+import android.net.UidRange;
+import android.net.QosSocketInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.INetworkActivityListener;
+import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+
+import com.android.connectivity.aidl.INetworkAgent;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+
+/**
+ * Interface that answers queries about, and allows changing, the
+ * state of network connectivity.
+ */
+/** {@hide} */
+interface IConnectivityManager
+{
+ Network getActiveNetwork();
+ Network getActiveNetworkForUid(int uid, boolean ignoreBlocked);
+ @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, String callingPackageName);
+
+ boolean isNetworkSupported(int networkType);
+
+ @UnsupportedAppUsage
+ LinkProperties getActiveLinkProperties();
+ LinkProperties getLinkPropertiesForType(int networkType);
+ LinkProperties getLinkProperties(in Network network);
+
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+
+ @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ NetworkState[] getAllNetworkState();
+
+ boolean isActiveNetworkMetered();
+
+ boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
+ String callingPackageName, String callingAttributionTag);
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getLastTetherError} as alternative")
+ int getLastTetherError(String iface);
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableIfaces} as alternative")
+ String[] getTetherableIfaces();
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetheredIfaces} as alternative")
+ String[] getTetheredIfaces();
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetheringErroredIfaces} "
+ + "as Alternative")
+ String[] getTetheringErroredIfaces();
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableUsbRegexs} as alternative")
+ String[] getTetherableUsbRegexs();
+
+ @UnsupportedAppUsage(maxTargetSdk = 29,
+ publicAlternatives = "Use {@code TetheringManager#getTetherableWifiRegexs} as alternative")
+ String[] getTetherableWifiRegexs();
+
+ @UnsupportedAppUsage(maxTargetSdk = 28)
+ void reportInetCondition(int networkType, int percentage);
+
+ void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
+
+ ProxyInfo getGlobalProxy();
+
+ void setGlobalProxy(in ProxyInfo p);
+
+ ProxyInfo getProxyForNetwork(in Network nework);
+
+ boolean prepareVpn(String oldPackage, String newPackage, int userId);
+
+ 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(maxTargetSdk = 30, trackingBug = 170729553)
+ void startLegacyVpn(in VpnProfile profile);
+
+ LegacyVpnInfo getLegacyVpnInfo(int userId);
+
+ boolean updateLockdownVpn();
+ boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
+ boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
+ in List<String> lockdownWhitelist);
+ String getAlwaysOnVpnPackage(int userId);
+ boolean isVpnLockdownEnabled(int userId);
+ List<String> getVpnLockdownWhitelist(int userId);
+ void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
+
+ void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
+
+ void setAirplaneMode(boolean enable);
+
+ boolean requestBandwidthUpdate(in Network network);
+
+ int registerNetworkFactory(in Messenger messenger, in String name);
+ void unregisterNetworkFactory(in Messenger messenger);
+
+ int registerNetworkProvider(in Messenger messenger, in String name);
+ void unregisterNetworkProvider(in Messenger messenger);
+
+ void declareNetworkRequestUnfulfillable(in NetworkRequest request);
+
+ Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp,
+ in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in int factorySerialNumber);
+
+ NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
+ in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
+ String callingPackageName, String callingAttributionTag);
+
+ NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
+ in PendingIntent operation, String callingPackageName, String callingAttributionTag);
+
+ void releasePendingNetworkRequest(in PendingIntent operation);
+
+ NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
+ in Messenger messenger, in IBinder binder, String callingPackageName);
+
+ void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
+ in PendingIntent operation, String callingPackageName);
+
+ void releaseNetworkRequest(in NetworkRequest networkRequest);
+
+ void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
+ void setAcceptPartialConnectivity(in Network network, boolean accept, boolean always);
+ void setAvoidUnvalidated(in Network network);
+ void startCaptivePortalApp(in Network network);
+ void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
+
+ boolean shouldAvoidBadWifi();
+ int getMultipathPreference(in Network Network);
+
+ NetworkRequest getDefaultRequest();
+
+ int getRestoreDefaultNetworkDelay(int networkType);
+
+ boolean addVpnAddress(String address, int prefixLength);
+ boolean removeVpnAddress(String address, int prefixLength);
+ boolean setUnderlyingNetworksForVpn(in Network[] networks);
+
+ void factoryReset();
+
+ void startNattKeepalive(in Network network, int intervalSeconds,
+ in ISocketKeepaliveCallback cb, String srcAddr, int srcPort, String dstAddr);
+
+ void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId,
+ int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr,
+ String dstAddr);
+
+ void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds,
+ in ISocketKeepaliveCallback cb);
+
+ void stopKeepalive(in Network network, int slot);
+
+ String getCaptivePortalServerUrl();
+
+ byte[] getNetworkWatchlistConfigHash();
+
+ int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
+ boolean isCallerCurrentAlwaysOnVpnApp();
+ boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+
+ 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);
+
+ void systemReady();
+
+ void registerNetworkActivityListener(in INetworkActivityListener l);
+
+ void unregisterNetworkActivityListener(in INetworkActivityListener l);
+
+ boolean isDefaultNetworkActive();
+
+ void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
+ void unregisterQosCallback(in IQosCallback callback);
+}
diff --git a/framework/src/android/net/ISocketKeepaliveCallback.aidl b/framework/src/android/net/ISocketKeepaliveCallback.aidl
new file mode 100644
index 0000000..020fbca
--- /dev/null
+++ b/framework/src/android/net/ISocketKeepaliveCallback.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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;
+
+/**
+ * Callback to provide status changes of keepalive offload.
+ *
+ * @hide
+ */
+oneway interface ISocketKeepaliveCallback
+{
+ /** The keepalive was successfully started. */
+ void onStarted(int slot);
+ /** The keepalive was successfully stopped. */
+ void onStopped();
+ /** The keepalive was stopped because of an error. */
+ void onError(int error);
+ /** The keepalive on a TCP socket was stopped because the socket received data. */
+ void onDataReceived();
+}
diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl
new file mode 100644
index 0000000..2a863ad
--- /dev/null
+++ b/framework/src/android/net/ITestNetworkManager.aidl
@@ -0,0 +1,39 @@
+/**
+ * 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 android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.TestNetworkInterface;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Interface that allows for creation and management of test-only networks.
+ *
+ * @hide
+ */
+interface ITestNetworkManager
+{
+ TestNetworkInterface createTunInterface(in LinkAddress[] linkAddrs);
+ TestNetworkInterface createTapInterface();
+
+ void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
+ in int[] administratorUids, in IBinder binder);
+
+ void teardownTestNetwork(int netId);
+}
diff --git a/framework/src/android/net/InetAddresses.java b/framework/src/android/net/InetAddresses.java
new file mode 100644
index 0000000..01b795e
--- /dev/null
+++ b/framework/src/android/net/InetAddresses.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.annotation.NonNull;
+
+import libcore.net.InetAddressUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Utility methods for {@link InetAddress} implementations.
+ */
+public class InetAddresses {
+
+ private InetAddresses() {}
+
+ /**
+ * Checks to see if the {@code address} is a numeric address (such as {@code "192.0.2.1"} or
+ * {@code "2001:db8::1:2"}).
+ *
+ * <p>A numeric address is either an IPv4 address containing exactly 4 decimal numbers or an
+ * IPv6 numeric address. IPv4 addresses that consist of either hexadecimal or octal digits or
+ * do not have exactly 4 numbers are not treated as numeric.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse.
+ * @return true if the supplied address is numeric, false otherwise.
+ */
+ public static boolean isNumericAddress(@NonNull String address) {
+ return InetAddressUtils.isNumericAddress(address);
+ }
+
+ /**
+ * Returns an InetAddress corresponding to the given numeric address (such
+ * as {@code "192.168.0.1"} or {@code "2001:4860:800d::68"}).
+ *
+ * <p>See {@link #isNumericAddress(String)} (String)} for a definition as to what constitutes a
+ * numeric address.
+ *
+ * <p>This method will never do a DNS lookup.
+ *
+ * @param address the address to parse, must be numeric.
+ * @return an {@link InetAddress} instance corresponding to the address.
+ * @throws IllegalArgumentException if {@code address} is not a numeric address.
+ */
+ public static @NonNull InetAddress parseNumericAddress(@NonNull String address) {
+ return InetAddressUtils.parseNumericAddress(address);
+ }
+}
diff --git a/framework/src/android/net/InterfaceConfiguration.aidl b/framework/src/android/net/InterfaceConfiguration.aidl
new file mode 100644
index 0000000..8aa5e34
--- /dev/null
+++ b/framework/src/android/net/InterfaceConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, 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;
+
+parcelable InterfaceConfiguration;
diff --git a/framework/src/android/net/InvalidPacketException.java b/framework/src/android/net/InvalidPacketException.java
new file mode 100644
index 0000000..1873d77
--- /dev/null
+++ b/framework/src/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/framework/src/android/net/IpConfiguration.aidl b/framework/src/android/net/IpConfiguration.aidl
new file mode 100644
index 0000000..7a30f0e
--- /dev/null
+++ b/framework/src/android/net/IpConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable IpConfiguration;
diff --git a/framework/src/android/net/IpConfiguration.java b/framework/src/android/net/IpConfiguration.java
new file mode 100644
index 0000000..0b20564
--- /dev/null
+++ b/framework/src/android/net/IpConfiguration.java
@@ -0,0 +1,223 @@
+/*
+ * 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.SuppressLint;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A class representing a configured network.
+ * @hide
+ */
+@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 */
+ STATIC,
+ /* Use dynamically configured IP settings */
+ DHCP,
+ /* no IP details are assigned, this is used to indicate
+ * that any existing IP settings should be retained */
+ 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. */
+ NONE,
+ /* Use statically configured proxy. Configuration can be accessed
+ * with httpProxy. */
+ STATIC,
+ /* no proxy details are assigned, this is used to indicate
+ * that any existing proxy settings should be retained */
+ UNASSIGNED,
+ /* Use a Pac based proxy.
+ */
+ PAC
+ }
+
+ /** @hide */
+ public ProxySettings proxySettings;
+
+ /** @hide */
+ @UnsupportedAppUsage
+ public ProxyInfo httpProxy;
+
+ private void init(IpAssignment ipAssignment,
+ ProxySettings proxySettings,
+ StaticIpConfiguration staticIpConfiguration,
+ ProxyInfo httpProxy) {
+ this.ipAssignment = ipAssignment;
+ this.proxySettings = proxySettings;
+ this.staticIpConfiguration = (staticIpConfiguration == null) ?
+ null : new StaticIpConfiguration(staticIpConfiguration);
+ this.httpProxy = (httpProxy == null) ?
+ null : new ProxyInfo(httpProxy);
+ }
+
+ public IpConfiguration() {
+ init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null);
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public IpConfiguration(IpAssignment ipAssignment,
+ ProxySettings proxySettings,
+ StaticIpConfiguration staticIpConfiguration,
+ ProxyInfo httpProxy) {
+ init(ipAssignment, proxySettings, staticIpConfiguration, httpProxy);
+ }
+
+ public IpConfiguration(@NonNull IpConfiguration source) {
+ this();
+ if (source != null) {
+ init(source.ipAssignment, source.proxySettings,
+ source.staticIpConfiguration, source.httpProxy);
+ }
+ }
+
+ public @NonNull IpAssignment getIpAssignment() {
+ return ipAssignment;
+ }
+
+ public void setIpAssignment(@NonNull IpAssignment ipAssignment) {
+ this.ipAssignment = ipAssignment;
+ }
+
+ public @Nullable StaticIpConfiguration getStaticIpConfiguration() {
+ return staticIpConfiguration;
+ }
+
+ public void setStaticIpConfiguration(@Nullable StaticIpConfiguration staticIpConfiguration) {
+ this.staticIpConfiguration = staticIpConfiguration;
+ }
+
+ public @NonNull ProxySettings getProxySettings() {
+ return proxySettings;
+ }
+
+ public void setProxySettings(@NonNull ProxySettings proxySettings) {
+ this.proxySettings = proxySettings;
+ }
+
+ public @Nullable ProxyInfo getHttpProxy() {
+ return httpProxy;
+ }
+
+ public void setHttpProxy(@Nullable ProxyInfo httpProxy) {
+ this.httpProxy = httpProxy;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append("IP assignment: " + ipAssignment.toString());
+ sbuf.append("\n");
+ if (staticIpConfiguration != null) {
+ sbuf.append("Static configuration: " + staticIpConfiguration.toString());
+ sbuf.append("\n");
+ }
+ sbuf.append("Proxy settings: " + proxySettings.toString());
+ sbuf.append("\n");
+ if (httpProxy != null) {
+ sbuf.append("HTTP proxy: " + httpProxy.toString());
+ sbuf.append("\n");
+ }
+
+ return sbuf.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof IpConfiguration)) {
+ return false;
+ }
+
+ IpConfiguration other = (IpConfiguration) o;
+ return this.ipAssignment == other.ipAssignment &&
+ this.proxySettings == other.proxySettings &&
+ Objects.equals(this.staticIpConfiguration, other.staticIpConfiguration) &&
+ Objects.equals(this.httpProxy, other.httpProxy);
+ }
+
+ @Override
+ public int hashCode() {
+ return 13 + (staticIpConfiguration != null ? staticIpConfiguration.hashCode() : 0) +
+ 17 * ipAssignment.ordinal() +
+ 47 * proxySettings.ordinal() +
+ 83 * httpProxy.hashCode();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(ipAssignment.name());
+ dest.writeString(proxySettings.name());
+ dest.writeParcelable(staticIpConfiguration, flags);
+ dest.writeParcelable(httpProxy, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<IpConfiguration> CREATOR =
+ new Creator<IpConfiguration>() {
+ public IpConfiguration createFromParcel(Parcel in) {
+ IpConfiguration config = new IpConfiguration();
+ config.ipAssignment = IpAssignment.valueOf(in.readString());
+ config.proxySettings = ProxySettings.valueOf(in.readString());
+ config.staticIpConfiguration = in.readParcelable(null);
+ config.httpProxy = in.readParcelable(null);
+ return config;
+ }
+
+ public IpConfiguration[] newArray(int size) {
+ return new IpConfiguration[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/IpPrefix.aidl b/framework/src/android/net/IpPrefix.aidl
new file mode 100644
index 0000000..0d70f2a
--- /dev/null
+++ b/framework/src/android/net/IpPrefix.aidl
@@ -0,0 +1,22 @@
+/**
+ *
+ * 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;
+
+// @JavaOnlyStableParcelable only affects the parcelable when built as stable aidl (aidl_interface
+// build rule). IpPrefix is also used in cpp but only as non-stable aidl.
+@JavaOnlyStableParcelable parcelable IpPrefix cpp_header "binder/IpPrefix.h";
diff --git a/framework/src/android/net/IpPrefix.java b/framework/src/android/net/IpPrefix.java
new file mode 100644
index 0000000..e7c8014
--- /dev/null
+++ b/framework/src/android/net/IpPrefix.java
@@ -0,0 +1,300 @@
+/*
+ * 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
+ * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of
+ * information:
+ *
+ * <ul>
+ * <li>A starting IP address (IPv4 or IPv6). This is the first IP address of the prefix.
+ * <li>A prefix length. This specifies the length of the prefix by specifing the number of bits
+ * in the IP address, starting from the most significant bit in network byte order, that
+ * are constant for all addresses in the prefix.
+ * </ul>
+ *
+ * For example, the prefix <code>192.0.2.0/24</code> covers the 256 IPv4 addresses from
+ * <code>192.0.2.0</code> to <code>192.0.2.255</code>, inclusive, and the prefix
+ * <code>2001:db8:1:2</code> covers the 2^64 IPv6 addresses from <code>2001:db8:1:2::</code> to
+ * <code>2001:db8:1:2:ffff:ffff:ffff:ffff</code>, inclusive.
+ *
+ * Objects of this class are immutable.
+ */
+public final class IpPrefix implements Parcelable {
+ private final byte[] address; // network byte order
+ private final int prefixLength;
+
+ private void checkAndMaskAddressAndPrefixLength() {
+ if (address.length != 4 && address.length != 16) {
+ throw new IllegalArgumentException(
+ "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
+ }
+ NetworkUtils.maskRawAddress(address, prefixLength);
+ }
+
+ /**
+ * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
+ * network byte order and a prefix length. Silently truncates the address to the prefix length,
+ * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ *
+ * @hide
+ */
+ public IpPrefix(@NonNull byte[] address, @IntRange(from = 0, to = 128) int prefixLength) {
+ this.address = address.clone();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently
+ * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently
+ * converted to {@code 192.0.2.0/24}.
+ *
+ * @param address the IP address. Must be non-null.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ * @hide
+ */
+ @SystemApi
+ public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) {
+ // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
+ // which is unnecessary because getAddress() already returns a clone.
+ this.address = address.getAddress();
+ this.prefixLength = prefixLength;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64".
+ * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24}
+ * is silently converted to {@code 192.0.2.0/24}.
+ *
+ * @param prefix the prefix to parse
+ *
+ * @hide
+ */
+ @SystemApi
+ public IpPrefix(@NonNull String prefix) {
+ // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
+ // first statement in constructor". We could factor out setting the member variables to an
+ // init() method, but if we did, then we'd have to make the members non-final, or "error:
+ // cannot assign a value to final variable address". So we just duplicate the code here.
+ Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
+ this.address = ipAndMask.first.getAddress();
+ this.prefixLength = ipAndMask.second;
+ checkAndMaskAddressAndPrefixLength();
+ }
+
+ /**
+ * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two
+ * objects are equal if they have the same startAddress and prefixLength.
+ *
+ * @param obj the object to be tested for equality.
+ * @return {@code true} if both objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IpPrefix)) {
+ return false;
+ }
+ IpPrefix that = (IpPrefix) obj;
+ return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength;
+ }
+
+ /**
+ * Gets the hashcode of the represented IP prefix.
+ *
+ * @return the appropriate hashcode value.
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(address) + 11 * prefixLength;
+ }
+
+ /**
+ * Returns a copy of the first IP address in the prefix. Modifying the returned object does not
+ * change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public @NonNull InetAddress getAddress() {
+ try {
+ return InetAddress.getByAddress(address);
+ } catch (UnknownHostException e) {
+ // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
+ // array is the wrong length, but we check that in the constructor.
+ throw new IllegalArgumentException("Address is invalid");
+ }
+ }
+
+ /**
+ * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth
+ * element). Modifying the returned array does not change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public @NonNull byte[] getRawAddress() {
+ return address.clone();
+ }
+
+ /**
+ * Returns the prefix length of this {@code IpPrefix}.
+ *
+ * @return the prefix length.
+ */
+ @IntRange(from = 0, to = 128)
+ public int getPrefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Determines whether the prefix contains the specified address.
+ *
+ * @param address An {@link InetAddress} to test.
+ * @return {@code true} if the prefix covers the given address. {@code false} otherwise.
+ */
+ public boolean contains(@NonNull InetAddress address) {
+ byte[] addrBytes = address.getAddress();
+ if (addrBytes == null || addrBytes.length != this.address.length) {
+ return false;
+ }
+ NetworkUtils.maskRawAddress(addrBytes, prefixLength);
+ return Arrays.equals(this.address, addrBytes);
+ }
+
+ /**
+ * Returns whether the specified prefix is entirely contained in this prefix.
+ *
+ * Note this is mathematical inclusion, so a prefix is always contained within itself.
+ * @param otherPrefix the prefix to test
+ * @hide
+ */
+ public boolean containsPrefix(@NonNull IpPrefix otherPrefix) {
+ if (otherPrefix.getPrefixLength() < prefixLength) return false;
+ final byte[] otherAddress = otherPrefix.getRawAddress();
+ NetworkUtils.maskRawAddress(otherAddress, prefixLength);
+ return Arrays.equals(otherAddress, address);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isIPv6() {
+ return getAddress() instanceof Inet6Address;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isIPv4() {
+ return getAddress() instanceof Inet4Address;
+ }
+
+ /**
+ * Returns a string representation of this {@code IpPrefix}.
+ *
+ * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}.
+ */
+ public String toString() {
+ try {
+ return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength;
+ } catch(UnknownHostException e) {
+ // Cosmic rays?
+ throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e);
+ }
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(address);
+ dest.writeInt(prefixLength);
+ }
+
+ /**
+ * Returns a comparator ordering IpPrefixes by length, shorter to longer.
+ * Contents of the address will break ties.
+ * @hide
+ */
+ public static Comparator<IpPrefix> lengthComparator() {
+ return new Comparator<IpPrefix>() {
+ @Override
+ public int compare(IpPrefix prefix1, IpPrefix prefix2) {
+ if (prefix1.isIPv4()) {
+ if (prefix2.isIPv6()) return -1;
+ } else {
+ if (prefix2.isIPv4()) return 1;
+ }
+ final int p1len = prefix1.getPrefixLength();
+ final int p2len = prefix2.getPrefixLength();
+ if (p1len < p2len) return -1;
+ if (p2len < p1len) return 1;
+ final byte[] a1 = prefix1.address;
+ final byte[] a2 = prefix2.address;
+ final int len = a1.length < a2.length ? a1.length : a2.length;
+ for (int i = 0; i < len; ++i) {
+ if (a1[i] < a2[i]) return -1;
+ if (a1[i] > a2[i]) return 1;
+ }
+ if (a2.length < len) return 1;
+ if (a1.length < len) return -1;
+ return 0;
+ }
+ };
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final @android.annotation.NonNull Creator<IpPrefix> CREATOR =
+ new Creator<IpPrefix>() {
+ public IpPrefix createFromParcel(Parcel in) {
+ byte[] address = in.createByteArray();
+ int prefixLength = in.readInt();
+ return new IpPrefix(address, prefixLength);
+ }
+
+ public IpPrefix[] newArray(int size) {
+ return new IpPrefix[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/KeepalivePacketData.aidl b/framework/src/android/net/KeepalivePacketData.aidl
new file mode 100644
index 0000000..d456b53
--- /dev/null
+++ b/framework/src/android/net/KeepalivePacketData.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable KeepalivePacketData;
diff --git a/framework/src/android/net/KeepalivePacketData.java b/framework/src/android/net/KeepalivePacketData.java
new file mode 100644
index 0000000..5877f1f
--- /dev/null
+++ b/framework/src/android/net/KeepalivePacketData.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.util.Log;
+
+import com.android.net.module.util.IpUtils;
+
+import java.net.InetAddress;
+
+/**
+ * Represents the actual packets that are sent by the
+ * {@link android.net.SocketKeepalive} API.
+ * @hide
+ */
+@SystemApi
+public class KeepalivePacketData {
+ private static final String TAG = "KeepalivePacketData";
+
+ /** Source IP address */
+ @NonNull
+ private final InetAddress mSrcAddress;
+
+ /** Destination IP address */
+ @NonNull
+ private final InetAddress mDstAddress;
+
+ /** Source port */
+ private final int mSrcPort;
+
+ /** Destination port */
+ private final int mDstPort;
+
+ /** Packet data. A raw byte string of packet data, not including the link-layer header. */
+ private final byte[] mPacket;
+
+ // 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.
+ /**
+ * 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.
+ if (srcAddress == null || dstAddress == null || !srcAddress.getClass().getName()
+ .equals(dstAddress.getClass().getName())) {
+ Log.e(TAG, "Invalid or mismatched InetAddresses in KeepalivePacketData");
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ // Check the ports.
+ if (!IpUtils.isValidUdpOrTcpPort(srcPort) || !IpUtils.isValidUdpOrTcpPort(dstPort)) {
+ Log.e(TAG, "Invalid ports in KeepalivePacketData");
+ throw new InvalidPacketException(ERROR_INVALID_PORT);
+ }
+ }
+
+ /** 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();
+ }
+
+}
diff --git a/framework/src/android/net/LinkAddress.aidl b/framework/src/android/net/LinkAddress.aidl
new file mode 100644
index 0000000..9c804db
--- /dev/null
+++ b/framework/src/android/net/LinkAddress.aidl
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright (C) 2010 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;
+
+@JavaOnlyStableParcelable parcelable LinkAddress;
+
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
new file mode 100644
index 0000000..44d25a1
--- /dev/null
+++ b/framework/src/android/net/LinkAddress.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2010 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.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;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+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;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * Identifies an IP address on a network link.
+ *
+ * A {@code LinkAddress} consists of:
+ * <ul>
+ * <li>An IP address and prefix length (e.g., {@code 2001:db8::1/64} or {@code 192.0.2.1/24}).
+ * The address must be unicast, as multicast addresses cannot be assigned to interfaces.
+ * <li>Address flags: A bitmask of {@code OsConstants.IFA_F_*} values representing properties
+ * of the address (e.g., {@code android.system.OsConstants.IFA_F_OPTIMISTIC}).
+ * <li>Address scope: One of the {@code OsConstants.IFA_F_*} values; defines the scope in which
+ * the address is unique (e.g.,
+ * {@code android.system.OsConstants.RT_SCOPE_LINK} or
+ * {@code android.system.OsConstants.RT_SCOPE_UNIVERSE}).
+ * </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.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private InetAddress address;
+
+ /**
+ * Prefix length.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private int prefixLength;
+
+ /**
+ * 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;
+
+ /**
+ * Address scope. One of the RT_SCOPE_* constants.
+ */
+ 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
+ */
+ private static int scopeForUnicastAddress(InetAddress addr) {
+ if (addr.isAnyLocalAddress()) {
+ return RT_SCOPE_HOST;
+ }
+
+ if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
+ return RT_SCOPE_LINK;
+ }
+
+ // isSiteLocalAddress() returns true for private IPv4 addresses, but RFC 6724 section 3.2
+ // says that they are assigned global scope.
+ if (!(addr instanceof Inet4Address) && addr.isSiteLocalAddress()) {
+ return RT_SCOPE_SITE;
+ }
+
+ return RT_SCOPE_UNIVERSE;
+ }
+
+ /**
+ * Utility function to check if |address| is a Unique Local IPv6 Unicast Address
+ * (a.k.a. "ULA"; RFC 4193).
+ *
+ * Per RFC 4193 section 8, fc00::/7 identifies these addresses.
+ */
+ private boolean isIpv6ULA() {
+ if (isIpv6()) {
+ byte[] bytes = address.getAddress();
+ return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the address is IPv6.
+ * @hide
+ */
+ @SystemApi
+ public boolean isIpv6() {
+ return address instanceof Inet6Address;
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return true if the address is IPv6.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean isIPv6() {
+ return isIpv6();
+ }
+
+ /**
+ * @return true if the address is IPv4 or is a mapped IPv4 address.
+ * @hide
+ */
+ @SystemApi
+ public boolean isIpv4() {
+ return address instanceof Inet4Address;
+ }
+
+ /**
+ * Utility function for the constructors.
+ */
+ private void init(InetAddress address, int prefixLength, int flags, int scope,
+ long deprecationTime, long expirationTime) {
+ if (address == null ||
+ address.isMulticastAddress() ||
+ prefixLength < 0 ||
+ (address instanceof Inet4Address && prefixLength > 32) ||
+ (prefixLength > 128)) {
+ 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.
+ * @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}).
+ * @hide
+ */
+ @SystemApi
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int 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
+ 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);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from an {@code InetAddress} and a prefix length.
+ * The flags are set to zero and the scope is determined from the address.
+ * @param address The IP address.
+ * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ * @hide
+ */
+ @SystemApi
+ public LinkAddress(@NonNull InetAddress address,
+ @IntRange(from = 0, to = 128) int prefixLength) {
+ this(address, prefixLength, 0, 0);
+ this.scope = scopeForUnicastAddress(address);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from an {@code InterfaceAddress}.
+ * The flags are set to zero and the scope is determined from the address.
+ * @param interfaceAddress The interface address.
+ * @hide
+ */
+ public LinkAddress(@NonNull InterfaceAddress interfaceAddress) {
+ this(interfaceAddress.getAddress(),
+ interfaceAddress.getNetworkPrefixLength());
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
+ * "2001:db8::1/64". The flags are set to zero and the scope is determined from the address.
+ * @param address The string to parse.
+ * @hide
+ */
+ @SystemApi
+ public LinkAddress(@NonNull String address) {
+ this(address, 0, 0);
+ this.scope = scopeForUnicastAddress(this.address);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
+ * "2001:db8::1/64", with the specified flags and scope.
+ * @param address The string to parse.
+ * @param flags The address flags.
+ * @param scope The address scope.
+ * @hide
+ */
+ @SystemApi
+ public LinkAddress(@NonNull String address, int flags, int scope) {
+ // 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, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
+ }
+
+ /**
+ * Returns a string representation of this address, such as "192.0.2.1/24" or "2001:db8::1/64".
+ * The string representation does not contain the flags and scope, just the address and prefix
+ * length.
+ */
+ @Override
+ public String toString() {
+ return address.getHostAddress() + "/" + prefixLength;
+ }
+
+ /**
+ * Compares this {@code LinkAddress} instance against {@code obj}. Two addresses are equal if
+ * their address, prefix length, flags and scope are equal. Thus, for example, two addresses
+ * that have the same address and prefix length are not equal if one of them is deprecated and
+ * the other is not.
+ *
+ * @param obj the object to be tested for equality.
+ * @return {@code true} if both objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof LinkAddress)) {
+ return false;
+ }
+ LinkAddress linkAddress = (LinkAddress) obj;
+ 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;
+ }
+
+ /**
+ * Returns a hashcode for this address.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLength, flags, scope, deprecationTime, expirationTime);
+ }
+
+ /**
+ * Determines whether this {@code LinkAddress} and the provided {@code LinkAddress}
+ * represent the same address. Two {@code LinkAddresses} represent the same address
+ * if they have the same IP address and prefix length, even if their properties are
+ * different.
+ *
+ * @param other the {@code LinkAddress} to compare to.
+ * @return {@code true} if both objects have the same address and prefix length, {@code false}
+ * otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isSameAddressAs(@Nullable LinkAddress other) {
+ if (other == null) {
+ return false;
+ }
+ return address.equals(other.address) && prefixLength == other.prefixLength;
+ }
+
+ /**
+ * Returns the {@link InetAddress} of this {@code LinkAddress}.
+ */
+ public InetAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * Returns the prefix length of this {@code LinkAddress}.
+ */
+ @IntRange(from = 0, to = 128)
+ public int getPrefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Returns the prefix length of this {@code LinkAddress}.
+ * TODO: Delete all callers and remove in favour of getPrefixLength().
+ * @hide
+ */
+ @UnsupportedAppUsage
+ @IntRange(from = 0, to = 128)
+ public int getNetworkPrefixLength() {
+ return getPrefixLength();
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the scope of this {@code LinkAddress}.
+ */
+ public int getScope() {
+ return scope;
+ }
+
+ /**
+ * 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
+ 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
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+
+ /**
+ * Returns true if this {@code LinkAddress} is global scope and preferred (i.e., not currently
+ * deprecated).
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isGlobalPreferred() {
+ /**
+ * Note that addresses flagged as IFA_F_OPTIMISTIC are
+ * simultaneously flagged as IFA_F_TENTATIVE (when the tentative
+ * 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
+ && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L));
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(address.getAddress());
+ dest.writeInt(prefixLength);
+ dest.writeInt(this.flags);
+ dest.writeInt(scope);
+ dest.writeLong(deprecationTime);
+ dest.writeLong(expirationTime);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final @android.annotation.NonNull Creator<LinkAddress> CREATOR =
+ new Creator<LinkAddress>() {
+ public LinkAddress createFromParcel(Parcel in) {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {
+ // Nothing we can do here. When we call the constructor, we'll throw an
+ // IllegalArgumentException, because a LinkAddress can't have a null
+ // InetAddress.
+ }
+ int prefixLength = in.readInt();
+ int flags = in.readInt();
+ int scope = in.readInt();
+ long deprecationTime = in.readLong();
+ long expirationTime = in.readLong();
+ return new LinkAddress(address, prefixLength, flags, scope, deprecationTime,
+ expirationTime);
+ }
+
+ public LinkAddress[] newArray(int size) {
+ return new LinkAddress[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/LinkProperties.aidl b/framework/src/android/net/LinkProperties.aidl
new file mode 100644
index 0000000..a8b3c7b
--- /dev/null
+++ b/framework/src/android/net/LinkProperties.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright (C) 2010 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;
+
+@JavaOnlyStableParcelable parcelable LinkProperties;
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
new file mode 100644
index 0000000..486e2d7
--- /dev/null
+++ b/framework/src/android/net/LinkProperties.java
@@ -0,0 +1,1823 @@
+/*
+ * Copyright (C) 2010 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.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.net.module.util.LinkPropertiesUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * Describes the properties of a network link.
+ *
+ * A link represents a connection to a network.
+ * It may have multiple addresses and multiple gateways,
+ * multiple dns servers but only one http proxy and one
+ * network interface.
+ *
+ * Note that this is just a holder of data. Modifying it
+ * does not affect live networks.
+ *
+ */
+public final class LinkProperties implements Parcelable {
+ // The interface described by the network link.
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private String mIfaceName;
+ private final ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
+ private final ArrayList<InetAddress> mDnses = new ArrayList<>();
+ // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service.
+ private final ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>();
+ private final ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
+ private boolean mUsePrivateDns;
+ 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;
+
+ private static final int MAX_MTU = 10000;
+
+ private static final int INET6_ADDR_LENGTH = 16;
+
+ // Stores the properties of links that are "stacked" above this link.
+ // Indexed by interface name to allow modification and to prevent duplicates being added.
+ private Hashtable<String, LinkProperties> mStackedLinks = new Hashtable<>();
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Landroid/net/LinkProperties$ProvisioningChange;")
+ public enum ProvisioningChange {
+ @UnsupportedAppUsage
+ STILL_NOT_PROVISIONED,
+ @UnsupportedAppUsage
+ LOST_PROVISIONING,
+ @UnsupportedAppUsage
+ GAINED_PROVISIONING,
+ @UnsupportedAppUsage
+ STILL_PROVISIONED,
+ }
+
+ /**
+ * Compare the provisioning states of two LinkProperties instances.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static ProvisioningChange compareProvisioning(
+ LinkProperties before, LinkProperties after) {
+ if (before.isProvisioned() && after.isProvisioned()) {
+ // On dual-stack networks, DHCPv4 renewals can occasionally fail.
+ // When this happens, IPv6-reachable services continue to function
+ // normally but IPv4-only services (naturally) fail.
+ //
+ // When an application using an IPv4-only service reports a bad
+ // network condition to the framework, attempts to re-validate
+ // the network succeed (since we support IPv6-only networks) and
+ // nothing is changed.
+ //
+ // For users, this is confusing and unexpected behaviour, and is
+ // not necessarily easy to diagnose. Therefore, we treat changing
+ // from a dual-stack network to an IPv6-only network equivalent to
+ // a total loss of provisioning.
+ //
+ // For one such example of this, see b/18867306.
+ //
+ // Additionally, losing IPv6 provisioning can result in TCP
+ // connections getting stuck until timeouts fire and other
+ // baffling failures. Therefore, loss of either IPv4 or IPv6 on a
+ // previously dual-stack network is deemed a lost of provisioning.
+ if ((before.isIpv4Provisioned() && !after.isIpv4Provisioned())
+ || (before.isIpv6Provisioned() && !after.isIpv6Provisioned())) {
+ return ProvisioningChange.LOST_PROVISIONING;
+ }
+ return ProvisioningChange.STILL_PROVISIONED;
+ } else if (before.isProvisioned() && !after.isProvisioned()) {
+ return ProvisioningChange.LOST_PROVISIONING;
+ } else if (!before.isProvisioned() && after.isProvisioned()) {
+ return ProvisioningChange.GAINED_PROVISIONING;
+ } else { // !before.isProvisioned() && !after.isProvisioned()
+ return ProvisioningChange.STILL_NOT_PROVISIONED;
+ }
+ }
+
+ /**
+ * Constructs a new {@code LinkProperties} with default values.
+ */
+ public LinkProperties() {
+ mParcelSensitiveFields = false;
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public LinkProperties(@Nullable LinkProperties source) {
+ 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
+ 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;
+ }
+
+ /**
+ * Sets the interface name for this link. All {@link RouteInfo} already set for this
+ * will have their interface changed to match this new value.
+ *
+ * @param iface The name of the network interface used for this link.
+ */
+ public void setInterfaceName(@Nullable String iface) {
+ mIfaceName = iface;
+ ArrayList<RouteInfo> newRoutes = new ArrayList<>(mRoutes.size());
+ for (RouteInfo route : mRoutes) {
+ newRoutes.add(routeWithInterface(route));
+ }
+ mRoutes = newRoutes;
+ }
+
+ /**
+ * Gets the interface name for this link. May be {@code null} if not set.
+ *
+ * @return The interface name set for this link or {@code null}.
+ */
+ public @Nullable String getInterfaceName() {
+ return mIfaceName;
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<String> getAllInterfaceNames() {
+ List<String> interfaceNames = new ArrayList<>(mStackedLinks.size() + 1);
+ if (mIfaceName != null) interfaceNames.add(mIfaceName);
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ interfaceNames.addAll(stacked.getAllInterfaceNames());
+ }
+ return interfaceNames;
+ }
+
+ /**
+ * Returns all the addresses on this link. We often think of a link having a single address,
+ * however, particularly with Ipv6 several addresses are typical. Note that the
+ * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include
+ * prefix lengths for each address. This is a simplified utility alternative to
+ * {@link LinkProperties#getLinkAddresses}.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for this link.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<InetAddress> getAddresses() {
+ final List<InetAddress> addresses = new ArrayList<>();
+ for (LinkAddress linkAddress : mLinkAddresses) {
+ addresses.add(linkAddress.getAddress());
+ }
+ return Collections.unmodifiableList(addresses);
+ }
+
+ /**
+ * Returns all the addresses on this link and all the links stacked above it.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @NonNull List<InetAddress> getAllAddresses() {
+ List<InetAddress> addresses = new ArrayList<>();
+ for (LinkAddress linkAddress : mLinkAddresses) {
+ addresses.add(linkAddress.getAddress());
+ }
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ addresses.addAll(stacked.getAllAddresses());
+ }
+ return addresses;
+ }
+
+ private int findLinkAddressIndex(LinkAddress address) {
+ for (int i = 0; i < mLinkAddresses.size(); i++) {
+ if (mLinkAddresses.get(i).isSameAddressAs(address)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the
+ * same address/prefix does not already exist. If it does exist it is replaced.
+ * @param address The {@code LinkAddress} to add.
+ * @return true if {@code address} was added or updated, false otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean addLinkAddress(@NonNull LinkAddress address) {
+ if (address == null) {
+ return false;
+ }
+ int i = findLinkAddressIndex(address);
+ if (i < 0) {
+ // Address was not present. Add it.
+ mLinkAddresses.add(address);
+ return true;
+ } else if (mLinkAddresses.get(i).equals(address)) {
+ // Address was present and has same properties. Do nothing.
+ return false;
+ } else {
+ // Address was present and has different properties. Update it.
+ mLinkAddresses.set(i, address);
+ return true;
+ }
+ }
+
+ /**
+ * Removes a {@link LinkAddress} from this {@code LinkProperties}. Specifically, matches
+ * and {@link LinkAddress} with the same address and prefix.
+ *
+ * @param toRemove A {@link LinkAddress} specifying the address to remove.
+ * @return true if the address was removed, false if it did not exist.
+ * @hide
+ */
+ @SystemApi
+ public boolean removeLinkAddress(@NonNull LinkAddress toRemove) {
+ int i = findLinkAddressIndex(toRemove);
+ if (i >= 0) {
+ mLinkAddresses.remove(i);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns all the {@link LinkAddress} on this link. Typically a link will have
+ * one IPv4 address and one or more IPv6 addresses.
+ *
+ * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
+ */
+ public @NonNull List<LinkAddress> getLinkAddresses() {
+ return Collections.unmodifiableList(mLinkAddresses);
+ }
+
+ /**
+ * Returns all the addresses on this link and all the links stacked above it.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<LinkAddress> getAllLinkAddresses() {
+ List<LinkAddress> addresses = new ArrayList<>(mLinkAddresses);
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ addresses.addAll(stacked.getAllLinkAddresses());
+ }
+ return addresses;
+ }
+
+ /**
+ * Replaces the {@link LinkAddress} in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link LinkAddress}.
+ *
+ * @param addresses The {@link Collection} of {@link LinkAddress} to set in this
+ * object.
+ */
+ public void setLinkAddresses(@NonNull Collection<LinkAddress> addresses) {
+ mLinkAddresses.clear();
+ for (LinkAddress address: addresses) {
+ addLinkAddress(address);
+ }
+ }
+
+ /**
+ * Adds the given {@link InetAddress} to the list of DNS servers, if not present.
+ *
+ * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
+ * @return true if the DNS server was added, false if it was already present.
+ * @hide
+ */
+ @SystemApi
+ public boolean addDnsServer(@NonNull InetAddress dnsServer) {
+ if (dnsServer != null && !mDnses.contains(dnsServer)) {
+ mDnses.add(dnsServer);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of DNS servers.
+ *
+ * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers.
+ * @return true if the DNS server was removed, false if it did not exist.
+ * @hide
+ */
+ @SystemApi
+ public boolean removeDnsServer(@NonNull InetAddress dnsServer) {
+ return mDnses.remove(dnsServer);
+ }
+
+ /**
+ * Replaces the DNS servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param dnsServers The {@link Collection} of DNS servers to set in this object.
+ */
+ public void setDnsServers(@NonNull Collection<InetAddress> dnsServers) {
+ mDnses.clear();
+ for (InetAddress dnsServer: dnsServers) {
+ addDnsServer(dnsServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for DNS servers on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for DNS servers on
+ * this link.
+ */
+ public @NonNull List<InetAddress> getDnsServers() {
+ return Collections.unmodifiableList(mDnses);
+ }
+
+ /**
+ * Set whether private DNS is currently in use on this network.
+ *
+ * @param usePrivateDns The private DNS state.
+ * @hide
+ */
+ @SystemApi
+ public void setUsePrivateDns(boolean usePrivateDns) {
+ mUsePrivateDns = usePrivateDns;
+ }
+
+ /**
+ * Returns whether private DNS is currently in use on this network. When
+ * private DNS is in use, applications must not send unencrypted DNS
+ * queries as doing so could reveal private user information. Furthermore,
+ * if private DNS is in use and {@link #getPrivateDnsServerName} is not
+ * {@code null}, DNS queries must be sent to the specified DNS server.
+ *
+ * @return {@code true} if private DNS is in use, {@code false} otherwise.
+ */
+ public boolean isPrivateDnsActive() {
+ return mUsePrivateDns;
+ }
+
+ /**
+ * Set the name of the private DNS server to which private DNS queries
+ * should be sent when in strict mode. This value should be {@code null}
+ * when private DNS is off or in opportunistic mode.
+ *
+ * @param privateDnsServerName The private DNS server name.
+ * @hide
+ */
+ @SystemApi
+ public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
+ mPrivateDnsServerName = privateDnsServerName;
+ }
+
+ /**
+ * 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
+ * queries are only sent if the hostname's certificate is valid. If
+ * {@code null} and {@link #isPrivateDnsActive} is {@code true}, private
+ * DNS is in opportunistic mode, and applications should ensure that DNS
+ * queries are encrypted and sent to a DNS server returned by
+ * {@link #getDnsServers}. System DNS will handle each of these cases
+ * correctly, but applications implementing their own DNS lookups must make
+ * sure to follow these requirements.
+ *
+ * @return The private DNS server name.
+ */
+ public @Nullable String getPrivateDnsServerName() {
+ return mPrivateDnsServerName;
+ }
+
+ /**
+ * Adds the given {@link InetAddress} to the list of validated private DNS servers,
+ * if not present. This is distinct from the server name in that these are actually
+ * resolved addresses.
+ *
+ * @param dnsServer The {@link InetAddress} to add to the list of validated private DNS servers.
+ * @return true if the DNS server was added, false if it was already present.
+ * @hide
+ */
+ public boolean addValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) {
+ if (dnsServer != null && !mValidatedPrivateDnses.contains(dnsServer)) {
+ mValidatedPrivateDnses.add(dnsServer);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of validated private DNS servers.
+ *
+ * @param dnsServer The {@link InetAddress} to remove from the list of validated private DNS
+ * servers.
+ * @return true if the DNS server was removed, false if it did not exist.
+ * @hide
+ */
+ public boolean removeValidatedPrivateDnsServer(@NonNull InetAddress dnsServer) {
+ return mValidatedPrivateDnses.remove(dnsServer);
+ }
+
+ /**
+ * Replaces the validated private DNS servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param dnsServers The {@link Collection} of validated private DNS servers to set in this
+ * object.
+ * @hide
+ */
+ @SystemApi
+ public void setValidatedPrivateDnsServers(@NonNull Collection<InetAddress> dnsServers) {
+ mValidatedPrivateDnses.clear();
+ for (InetAddress dnsServer: dnsServers) {
+ addValidatedPrivateDnsServer(dnsServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for validated private DNS servers on this link.
+ * These are resolved from the private DNS server name.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for validated private
+ * DNS servers on this link.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<InetAddress> getValidatedPrivateDnsServers() {
+ return Collections.unmodifiableList(mValidatedPrivateDnses);
+ }
+
+ /**
+ * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present.
+ *
+ * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers.
+ * @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);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of PCSCF servers.
+ *
+ * @param pcscfServer The {@link InetAddress} to remove from the list of PCSCF servers.
+ * @return true if the PCSCF server was removed, false otherwise.
+ * @hide
+ */
+ public boolean removePcscfServer(@NonNull InetAddress pcscfServer) {
+ return mPcscfs.remove(pcscfServer);
+ }
+
+ /**
+ * Replaces the PCSCF servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param pcscfServers The {@link Collection} of PCSCF servers to set in this object.
+ * @hide
+ */
+ @SystemApi
+ public void setPcscfServers(@NonNull Collection<InetAddress> pcscfServers) {
+ mPcscfs.clear();
+ for (InetAddress pcscfServer: pcscfServers) {
+ addPcscfServer(pcscfServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for PCSCF servers on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on
+ * this link.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<InetAddress> getPcscfServers() {
+ return Collections.unmodifiableList(mPcscfs);
+ }
+
+ /**
+ * Sets the DNS domain search path used on this link.
+ *
+ * @param domains A {@link String} listing in priority order the comma separated
+ * domains to search when resolving host names on this link.
+ */
+ public void setDomains(@Nullable String domains) {
+ mDomains = domains;
+ }
+
+ /**
+ * Get the DNS domains search path set for this link. May be {@code null} if not set.
+ *
+ * @return A {@link String} containing the comma separated domains to search when resolving host
+ * names on this link or {@code null}.
+ */
+ public @Nullable String getDomains() {
+ return mDomains;
+ }
+
+ /**
+ * Sets the Maximum Transmission Unit size to use on this link. This should not be used
+ * unless the system default (1500) is incorrect. Values less than 68 or greater than
+ * 10000 will be ignored.
+ *
+ * @param mtu The MTU to use for this link.
+ */
+ public void setMtu(int mtu) {
+ mMtu = mtu;
+ }
+
+ /**
+ * Gets any non-default MTU size set for this link. Note that if the default is being used
+ * this will return 0.
+ *
+ * @return The mtu value set for this link.
+ */
+ public int getMtu() {
+ return mMtu;
+ }
+
+ /**
+ * Sets the tcp buffers sizes to be used when this link is the system default.
+ * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max".
+ *
+ * @param tcpBufferSizes The tcp buffers sizes to use.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setTcpBufferSizes(@Nullable String tcpBufferSizes) {
+ mTcpBufferSizes = tcpBufferSizes;
+ }
+
+ /**
+ * Gets the tcp buffer sizes. May be {@code null} if not set.
+ *
+ * @return the tcp buffer sizes to use when this link is the system default or {@code null}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getTcpBufferSizes() {
+ return mTcpBufferSizes;
+ }
+
+ private RouteInfo routeWithInterface(RouteInfo route) {
+ return new RouteInfo(
+ route.getDestination(),
+ route.getGateway(),
+ mIfaceName,
+ 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 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 true} was added or updated, false otherwise.
+ */
+ public boolean addRoute(@NonNull RouteInfo route) {
+ String routeIface = route.getInterface();
+ if (routeIface != null && !routeIface.equals(mIfaceName)) {
+ throw new IllegalArgumentException(
+ "Route added with non-matching interface: " + routeIface
+ + " vs. " + mIfaceName);
+ }
+ route = routeWithInterface(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;
+ }
+ }
+
+ /**
+ * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must
+ * 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
+ */
+ @SystemApi
+ public boolean removeRoute(@NonNull RouteInfo route) {
+ return Objects.equals(mIfaceName, route.getInterface()) && mRoutes.remove(route);
+ }
+
+ /**
+ * Returns all the {@link RouteInfo} set on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
+ */
+ public @NonNull List<RouteInfo> getRoutes() {
+ return Collections.unmodifiableList(mRoutes);
+ }
+
+ /**
+ * Make sure this LinkProperties instance contains routes that cover the local subnet
+ * of its link addresses. Add any route that is missing.
+ * @hide
+ */
+ public void ensureDirectlyConnectedRoutes() {
+ for (LinkAddress addr : mLinkAddresses) {
+ addRoute(new RouteInfo(addr, null, mIfaceName));
+ }
+ }
+
+ /**
+ * Returns all the routes on this link and all the links stacked above it.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull List<RouteInfo> getAllRoutes() {
+ List<RouteInfo> routes = new ArrayList<>(mRoutes);
+ for (LinkProperties stacked: mStackedLinks.values()) {
+ routes.addAll(stacked.getAllRoutes());
+ }
+ return routes;
+ }
+
+ /**
+ * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none.
+ * Note that Http Proxies are only a hint - the system recommends their use, but it does
+ * not enforce it and applications may ignore them.
+ *
+ * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link.
+ */
+ public void setHttpProxy(@Nullable ProxyInfo proxy) {
+ mHttpProxy = proxy;
+ }
+
+ /**
+ * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link.
+ *
+ * @return The {@link ProxyInfo} set on this link or {@code null}.
+ */
+ public @Nullable ProxyInfo getHttpProxy() {
+ return mHttpProxy;
+ }
+
+ /**
+ * Returns the NAT64 prefix in use on this link, if any.
+ *
+ * @return the NAT64 prefix or {@code null}.
+ */
+ public @Nullable IpPrefix getNat64Prefix() {
+ return mNat64Prefix;
+ }
+
+ /**
+ * Sets the NAT64 prefix in use on this link.
+ *
+ * Currently, only 96-bit prefixes (i.e., where the 32-bit IPv4 address is at the end of the
+ * 128-bit IPv6 address) are supported or {@code null} for no prefix.
+ *
+ * @param prefix the NAT64 prefix.
+ */
+ public void setNat64Prefix(@Nullable IpPrefix prefix) {
+ if (prefix != null && prefix.getPrefixLength() != 96) {
+ throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
+ }
+ mNat64Prefix = prefix; // IpPrefix objects are immutable.
+ }
+
+ /**
+ * Adds a stacked link.
+ *
+ * If there is already a stacked link with the same interface name as link,
+ * that link is replaced with link. Otherwise, link is added to the list
+ * of stacked links.
+ *
+ * @param link The link to add.
+ * @return true if the link was stacked, false otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean addStackedLink(@NonNull LinkProperties link) {
+ if (link.getInterfaceName() != null) {
+ mStackedLinks.put(link.getInterfaceName(), link);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a stacked link.
+ *
+ * If there is a stacked link with the given interface name, it is
+ * removed. Otherwise, nothing changes.
+ *
+ * @param iface The interface name of the link to remove.
+ * @return true if the link was removed, false otherwise.
+ * @hide
+ */
+ public boolean removeStackedLink(@NonNull String iface) {
+ LinkProperties removed = mStackedLinks.remove(iface);
+ return removed != null;
+ }
+
+ /**
+ * Returns all the links stacked on top of this link.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public @NonNull List<LinkProperties> getStackedLinks() {
+ if (mStackedLinks.isEmpty()) {
+ return Collections.emptyList();
+ }
+ final List<LinkProperties> stacked = new ArrayList<>();
+ for (LinkProperties link : mStackedLinks.values()) {
+ stacked.add(new LinkProperties(link));
+ }
+ return Collections.unmodifiableList(stacked);
+ }
+
+ /**
+ * 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();
+ mUsePrivateDns = false;
+ mPrivateDnsServerName = null;
+ mPcscfs.clear();
+ mDomains = null;
+ mRoutes.clear();
+ mHttpProxy = null;
+ mStackedLinks.clear();
+ mMtu = 0;
+ mDhcpServerAddress = null;
+ mTcpBufferSizes = null;
+ mNat64Prefix = null;
+ mWakeOnLanSupported = false;
+ mCaptivePortalApiUrl = null;
+ mCaptivePortalData = null;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ // Space as a separator, so no need for spaces at start/end of the individual fragments.
+ final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
+
+ if (mIfaceName != null) {
+ resultJoiner.add("InterfaceName:");
+ resultJoiner.add(mIfaceName);
+ }
+
+ resultJoiner.add("LinkAddresses: [");
+ if (!mLinkAddresses.isEmpty()) {
+ resultJoiner.add(TextUtils.join(",", mLinkAddresses));
+ }
+ resultJoiner.add("]");
+
+ resultJoiner.add("DnsAddresses: [");
+ if (!mDnses.isEmpty()) {
+ resultJoiner.add(TextUtils.join(",", mDnses));
+ }
+ resultJoiner.add("]");
+
+ if (mUsePrivateDns) {
+ resultJoiner.add("UsePrivateDns: true");
+ }
+
+ if (mPrivateDnsServerName != null) {
+ resultJoiner.add("PrivateDnsServerName:");
+ resultJoiner.add(mPrivateDnsServerName);
+ }
+
+ if (!mPcscfs.isEmpty()) {
+ resultJoiner.add("PcscfAddresses: [");
+ resultJoiner.add(TextUtils.join(",", mPcscfs));
+ resultJoiner.add("]");
+ }
+
+ if (!mValidatedPrivateDnses.isEmpty()) {
+ final StringJoiner validatedPrivateDnsesJoiner =
+ new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
+ for (final InetAddress addr : mValidatedPrivateDnses) {
+ validatedPrivateDnsesJoiner.add(addr.getHostAddress());
+ }
+ resultJoiner.add(validatedPrivateDnsesJoiner.toString());
+ }
+
+ resultJoiner.add("Domains:");
+ resultJoiner.add(mDomains);
+
+ 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);
+ }
+
+ resultJoiner.add("Routes: [");
+ if (!mRoutes.isEmpty()) {
+ resultJoiner.add(TextUtils.join(",", mRoutes));
+ }
+ resultJoiner.add("]");
+
+ if (mHttpProxy != null) {
+ resultJoiner.add("HttpProxy:");
+ resultJoiner.add(mHttpProxy.toString());
+ }
+
+ if (mNat64Prefix != null) {
+ resultJoiner.add("Nat64Prefix:");
+ resultJoiner.add(mNat64Prefix.toString());
+ }
+
+ final Collection<LinkProperties> stackedLinksValues = mStackedLinks.values();
+ if (!stackedLinksValues.isEmpty()) {
+ final StringJoiner stackedLinksJoiner = new StringJoiner(",", "Stacked: [", "]");
+ for (final LinkProperties lp : stackedLinksValues) {
+ stackedLinksJoiner.add("[ " + lp + " ]");
+ }
+ resultJoiner.add(stackedLinksJoiner.toString());
+ }
+
+ return resultJoiner.toString();
+ }
+
+ /**
+ * Returns true if this link has an IPv4 address.
+ *
+ * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasIpv4Address() {
+ for (LinkAddress address : mLinkAddresses) {
+ if (address.getAddress() instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasIPv4Address() {
+ return hasIpv4Address();
+ }
+
+ /**
+ * Returns true if this link or any of its stacked interfaces has an IPv4 address.
+ *
+ * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ */
+ private boolean hasIpv4AddressOnInterface(String iface) {
+ // mIfaceName can be null.
+ return (Objects.equals(iface, mIfaceName) && hasIpv4Address())
+ || (iface != null && mStackedLinks.containsKey(iface)
+ && mStackedLinks.get(iface).hasIpv4Address());
+ }
+
+ /**
+ * Returns true if this link has a global preferred IPv6 address.
+ *
+ * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasGlobalIpv6Address() {
+ for (LinkAddress address : mLinkAddresses) {
+ if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasGlobalIPv6Address() {
+ return hasGlobalIpv6Address();
+ }
+
+ /**
+ * Returns true if this link has an IPv4 default route.
+ *
+ * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasIpv4DefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv4Default()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ * @return {@code true} if there is an IPv4 default route, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasIPv4DefaultRoute() {
+ return hasIpv4DefaultRoute();
+ }
+
+ /**
+ * Returns true if this link has an IPv6 default route.
+ *
+ * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasIpv6DefaultRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.isIPv6Default()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasIPv6DefaultRoute() {
+ return hasIpv6DefaultRoute();
+ }
+
+ /**
+ * Returns true if this link has an IPv4 DNS server.
+ *
+ * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasIpv4DnsServer() {
+ for (InetAddress ia : mDnses) {
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasIPv4DnsServer() {
+ return hasIpv4DnsServer();
+ }
+
+ /**
+ * Returns true if this link has an IPv6 DNS server.
+ *
+ * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean hasIpv6DnsServer() {
+ for (InetAddress ia : mDnses) {
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean hasIPv6DnsServer() {
+ return hasIpv6DnsServer();
+ }
+
+ /**
+ * Returns true if this link has an IPv4 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIpv4PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv6 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIpv6PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link is provisioned for global IPv4 connectivity.
+ * This requires an IP address, default route, and DNS server.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isIpv4Provisioned() {
+ return (hasIpv4Address()
+ && hasIpv4DefaultRoute()
+ && hasIpv4DnsServer());
+ }
+
+ /**
+ * Returns true if this link is provisioned for global IPv6 connectivity.
+ * This requires an IP address, default route, and DNS server.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isIpv6Provisioned() {
+ return (hasGlobalIpv6Address()
+ && hasIpv6DefaultRoute()
+ && hasIpv6DnsServer());
+ }
+
+ /**
+ * For backward compatibility.
+ * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
+ * just yet.
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public boolean isIPv6Provisioned() {
+ return isIpv6Provisioned();
+ }
+
+
+ /**
+ * Returns true if this link is provisioned for global connectivity,
+ * for at least one Internet Protocol family.
+ *
+ * @return {@code true} if the link is provisioned, {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isProvisioned() {
+ return (isIpv4Provisioned() || isIpv6Provisioned());
+ }
+
+ /**
+ * Evaluate whether the {@link InetAddress} is considered reachable.
+ *
+ * @return {@code true} if the given {@link InetAddress} is considered reachable,
+ * {@code false} otherwise.
+ * @hide
+ */
+ @SystemApi
+ public boolean isReachable(@NonNull InetAddress ip) {
+ final List<RouteInfo> allRoutes = getAllRoutes();
+ // If we don't have a route to this IP address, it's not reachable.
+ final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip);
+ if (bestRoute == null) {
+ return false;
+ }
+
+ // TODO: better source address evaluation for destination addresses.
+
+ if (ip instanceof Inet4Address) {
+ // For IPv4, it suffices for now to simply have any address.
+ return hasIpv4AddressOnInterface(bestRoute.getInterface());
+ } else if (ip instanceof Inet6Address) {
+ if (ip.isLinkLocalAddress()) {
+ // For now, just make sure link-local destinations have
+ // scopedIds set, since transmits will generally fail otherwise.
+ // TODO: verify it matches the ifindex of one of the interfaces.
+ return (((Inet6Address)ip).getScopeId() != 0);
+ } else {
+ // For non-link-local destinations check that either the best route
+ // is directly connected or that some global preferred address exists.
+ // TODO: reconsider all cases (disconnected ULA networks, ...).
+ return (!bestRoute.hasGateway() || hasGlobalIpv6Address());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compares this {@code LinkProperties} interface name against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
+ 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);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} interface addresses against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
+ return LinkPropertiesUtils.isIdenticalAddresses(target, this);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} DNS addresses against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isIdenticalDnses(@NonNull LinkProperties target) {
+ return LinkPropertiesUtils.isIdenticalDnses(target, this);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} private DNS settings against the
+ * target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPrivateDns(@NonNull LinkProperties target) {
+ return (isPrivateDnsActive() == target.isPrivateDnsActive()
+ && TextUtils.equals(getPrivateDnsServerName(),
+ target.getPrivateDnsServerName()));
+ }
+
+ /**
+ * Compares this {@code LinkProperties} validated private DNS addresses against
+ * the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalValidatedPrivateDnses(@NonNull LinkProperties target) {
+ Collection<InetAddress> targetDnses = target.getValidatedPrivateDnsServers();
+ return (mValidatedPrivateDnses.size() == targetDnses.size())
+ ? mValidatedPrivateDnses.containsAll(targetDnses) : false;
+ }
+
+ /**
+ * Compares this {@code LinkProperties} PCSCF addresses against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPcscfs(@NonNull LinkProperties target) {
+ Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+ return (mPcscfs.size() == targetPcscfs.size()) ?
+ mPcscfs.containsAll(targetPcscfs) : false;
+ }
+
+ /**
+ * Compares this {@code LinkProperties} Routes against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
+ return LinkPropertiesUtils.isIdenticalRoutes(target, this);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} HttpProxy against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
+ return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} stacked links against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public boolean isIdenticalStackedLinks(@NonNull LinkProperties target) {
+ if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) {
+ return false;
+ }
+ for (LinkProperties stacked : mStackedLinks.values()) {
+ // Hashtable values can never be null.
+ String iface = stacked.getInterfaceName();
+ if (!stacked.equals(target.mStackedLinks.get(iface))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compares this {@code LinkProperties} MTU against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalMtu(@NonNull LinkProperties target) {
+ return getMtu() == target.getMtu();
+ }
+
+ /**
+ * Compares this {@code LinkProperties} Tcp buffer sizes against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalTcpBufferSizes(@NonNull LinkProperties target) {
+ return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes);
+ }
+
+ /**
+ * Compares this {@code LinkProperties} NAT64 prefix against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalNat64Prefix(@NonNull LinkProperties target) {
+ return Objects.equals(mNat64Prefix, target.mNat64Prefix);
+ }
+
+ /**
+ * 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
+ 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
+ @Nullable
+ public Uri getCaptivePortalApiUrl() {
+ return mCaptivePortalApiUrl;
+ }
+
+ /**
+ * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+ * @hide
+ */
+ @SystemApi
+ 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
+ @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.
+ *
+ * For collection fields, such as mDnses, containsAll() is used to check
+ * if two collections contains the same elements, independent of order.
+ * There are two thoughts regarding containsAll()
+ * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
+ * 2. Worst case performance is O(n^2).
+ *
+ * @param obj the object to be tested for equality.
+ * @return {@code true} if both objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof LinkProperties)) return false;
+
+ LinkProperties target = (LinkProperties) obj;
+ /*
+ * This method does not check that stacked interfaces are equal, because
+ * stacked interfaces are not so much a property of the link as a
+ * description of connections between links.
+ */
+ return isIdenticalInterfaceName(target)
+ && isIdenticalAddresses(target)
+ && isIdenticalDhcpServerAddress(target)
+ && isIdenticalDnses(target)
+ && isIdenticalPrivateDns(target)
+ && isIdenticalValidatedPrivateDnses(target)
+ && isIdenticalPcscfs(target)
+ && isIdenticalRoutes(target)
+ && isIdenticalHttpProxy(target)
+ && isIdenticalStackedLinks(target)
+ && isIdenticalMtu(target)
+ && isIdenticalTcpBufferSizes(target)
+ && isIdenticalNat64Prefix(target)
+ && isIdenticalWakeOnLan(target)
+ && isIdenticalCaptivePortalApiUrl(target)
+ && isIdenticalCaptivePortalData(target);
+ }
+
+ /**
+ * Generate hashcode based on significant fields
+ *
+ * Equal objects must produce the same hash code, while unequal objects
+ * may have the same hash codes.
+ */
+ @Override
+ public int hashCode() {
+ return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+ + mLinkAddresses.size() * 31
+ + mDnses.size() * 37
+ + mValidatedPrivateDnses.size() * 61
+ + ((null == mDomains) ? 0 : mDomains.hashCode())
+ + mRoutes.size() * 41
+ + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
+ + mStackedLinks.hashCode() * 47)
+ + 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)
+ + (mWakeOnLanSupported ? 71 : 0)
+ + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(getInterfaceName());
+ dest.writeInt(mLinkAddresses.size());
+ for (LinkAddress linkAddress : mLinkAddresses) {
+ dest.writeParcelable(linkAddress, flags);
+ }
+
+ writeAddresses(dest, mDnses);
+ writeAddresses(dest, mValidatedPrivateDnses);
+ dest.writeBoolean(mUsePrivateDns);
+ dest.writeString(mPrivateDnsServerName);
+ writeAddresses(dest, mPcscfs);
+ dest.writeString(mDomains);
+ writeAddress(dest, mDhcpServerAddress);
+ dest.writeInt(mMtu);
+ dest.writeString(mTcpBufferSizes);
+ dest.writeInt(mRoutes.size());
+ for (RouteInfo route : mRoutes) {
+ dest.writeParcelable(route, flags);
+ }
+
+ if (mHttpProxy != null) {
+ dest.writeByte((byte)1);
+ dest.writeParcelable(mHttpProxy, flags);
+ } else {
+ dest.writeByte((byte)0);
+ }
+ dest.writeParcelable(mNat64Prefix, 0);
+
+ 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) {
+ dest.writeInt(list.size());
+ for (InetAddress d : list) {
+ writeAddress(dest, d);
+ }
+ }
+
+ 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;
+ dest.writeBoolean(hasScopeId);
+ if (hasScopeId) dest.writeInt(v6Addr.getScopeId());
+ }
+ }
+
+ @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;
+ return Inet6Address.getByAddress(null /* host */, addr, scopeId);
+ }
+
+ return InetAddress.getByAddress(addr);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final @android.annotation.NonNull Creator<LinkProperties> CREATOR =
+ new Creator<LinkProperties>() {
+ public LinkProperties createFromParcel(Parcel in) {
+ LinkProperties netProp = new LinkProperties();
+
+ String iface = in.readString();
+ if (iface != null) {
+ netProp.setInterfaceName(iface);
+ }
+ int addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ netProp.addLinkAddress(in.readParcelable(null));
+ }
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addDnsServer(readAddress(in));
+ } catch (UnknownHostException e) { }
+ }
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addValidatedPrivateDnsServer(readAddress(in));
+ } catch (UnknownHostException e) { }
+ }
+ netProp.setUsePrivateDns(in.readBoolean());
+ netProp.setPrivateDnsServerName(in.readString());
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addPcscfServer(readAddress(in));
+ } 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();
+ for (int i = 0; i < addressCount; i++) {
+ netProp.addRoute(in.readParcelable(null));
+ }
+ if (in.readByte() == 1) {
+ netProp.setHttpProxy(in.readParcelable(null));
+ }
+ netProp.setNat64Prefix(in.readParcelable(null));
+ ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
+ in.readList(stackedLinks, LinkProperties.class.getClassLoader());
+ for (LinkProperties stackedLink: stackedLinks) {
+ netProp.addStackedLink(stackedLink);
+ }
+ netProp.setWakeOnLanSupported(in.readBoolean());
+
+ netProp.setCaptivePortalApiUrl(in.readParcelable(null));
+ netProp.setCaptivePortalData(in.readParcelable(null));
+ return netProp;
+ }
+
+ public LinkProperties[] newArray(int size) {
+ return new LinkProperties[size];
+ }
+ };
+
+ /**
+ * Check the valid MTU range based on IPv4 or IPv6.
+ * @hide
+ */
+ public static boolean isValidMtu(int mtu, boolean ipv6) {
+ if (ipv6) {
+ return mtu >= MIN_MTU_V6 && mtu <= MAX_MTU;
+ } else {
+ return mtu >= MIN_MTU && mtu <= MAX_MTU;
+ }
+ }
+}
diff --git a/framework/src/android/net/MacAddress.aidl b/framework/src/android/net/MacAddress.aidl
new file mode 100644
index 0000000..48a18a7
--- /dev/null
+++ b/framework/src/android/net/MacAddress.aidl
@@ -0,0 +1,20 @@
+/**
+ *
+ * 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;
+
+@JavaOnlyStableParcelable parcelable MacAddress;
diff --git a/framework/src/android/net/MacAddress.java b/framework/src/android/net/MacAddress.java
new file mode 100644
index 0000000..c7116b4
--- /dev/null
+++ b/framework/src/android/net/MacAddress.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2017 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.compat.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiInfo;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+import com.android.net.module.util.MacAddressUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+/**
+ * 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. 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 {
+
+ private static final int ETHER_ADDR_LEN = 6;
+ private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+
+ /**
+ * The MacAddress representing the unique broadcast MAC address.
+ */
+ public static final MacAddress BROADCAST_ADDRESS = MacAddress.fromBytes(ETHER_ADDR_BROADCAST);
+
+ /**
+ * The MacAddress zero MAC address.
+ *
+ * <p>Not publicly exposed or treated specially since the OUI 00:00:00 is registered.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_UNKNOWN,
+ TYPE_UNICAST,
+ TYPE_MULTICAST,
+ TYPE_BROADCAST,
+ })
+ public @interface MacAddressType { }
+
+ /** @hide Indicates a MAC address of unknown type. */
+ public static final int TYPE_UNKNOWN = 0;
+ /** Indicates a MAC address is a unicast address. */
+ public static final int TYPE_UNICAST = 1;
+ /** Indicates a MAC address is a multicast address. */
+ public static final int TYPE_MULTICAST = 2;
+ /** Indicates a MAC address is the broadcast address. */
+ public static final int TYPE_BROADCAST = 3;
+
+ private static final long VALID_LONG_MASK = (1L << 48) - 1;
+ private static final long LOCALLY_ASSIGNED_MASK = MacAddress.fromString("2:0:0:0:0:0").mAddr;
+ private static final long MULTICAST_MASK = MacAddress.fromString("1:0:0:0:0:0").mAddr;
+ 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
+ // MAC address are encoded in the 6 least significant bytes of the long, where the first
+ // byte of the array is mapped to the 3rd highest logical byte of the long, the second
+ // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
+ private final long mAddr;
+
+ private MacAddress(long addr) {
+ mAddr = (VALID_LONG_MASK & addr);
+ }
+
+ /**
+ * Returns the type of this address.
+ *
+ * @return the int constant representing the MAC address type of this MacAddress.
+ */
+ public @MacAddressType int getAddressType() {
+ if (equals(BROADCAST_ADDRESS)) {
+ return TYPE_BROADCAST;
+ }
+ if ((mAddr & MULTICAST_MASK) != 0) {
+ return TYPE_MULTICAST;
+ }
+ return TYPE_UNICAST;
+ }
+
+ /**
+ * @return true if this MacAddress is a locally assigned address.
+ */
+ public boolean isLocallyAssigned() {
+ return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal
+ * numbers in [0,ff] joined by ':' characters.
+ */
+ public @NonNull String toOuiString() {
+ return String.format(
+ "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff);
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) ((mAddr >> 32) ^ mAddr);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mAddr);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<MacAddress> CREATOR =
+ new Parcelable.Creator<MacAddress>() {
+ public MacAddress createFromParcel(Parcel in) {
+ return new MacAddress(in.readLong());
+ }
+
+ public MacAddress[] newArray(int size) {
+ return new MacAddress[size];
+ }
+ };
+
+ /**
+ * Returns true if the given byte array is an valid MAC address.
+ * A valid byte array representation for a MacAddress is a non-null array of length 6.
+ *
+ * @param addr a byte array.
+ * @return true if the given byte array is not null and has the length of a MAC address.
+ *
+ * @hide
+ */
+ public static boolean isMacAddress(byte[] addr) {
+ return MacAddressUtils.isMacAddress(addr);
+ }
+
+ /**
+ * Returns the MAC address type of the MAC address represented by the given byte array,
+ * or null if the given byte array does not represent a MAC address.
+ * A valid byte array representation for a MacAddress is a non-null array of length 6.
+ *
+ * @param addr a byte array representing a MAC address.
+ * @return the int constant representing the MAC address type of the MAC address represented
+ * by the given byte array, or type UNKNOWN if the byte array is not a valid MAC address.
+ *
+ * @hide
+ */
+ public static int macAddressType(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return TYPE_UNKNOWN;
+ }
+ return MacAddress.fromBytes(addr).getAddressType();
+ }
+
+ /**
+ * Converts a String representation of a MAC address to a byte array representation.
+ * A valid String representation for a MacAddress is a series of 6 values in the
+ * range [0,ff] printed in hexadecimal and joined by ':' characters.
+ *
+ * @param addr a String representation of a MAC address.
+ * @return the byte representation of the MAC address.
+ * @throws IllegalArgumentException if the given String is not a valid representation.
+ *
+ * @hide
+ */
+ public static @NonNull byte[] byteAddrFromStringAddr(String addr) {
+ Preconditions.checkNotNull(addr);
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ int x = Integer.valueOf(parts[i], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ bytes[i] = (byte) x;
+ }
+ return bytes;
+ }
+
+ /**
+ * Converts a byte array representation of a MAC address to a String representation made
+ * of 6 hexadecimal numbers in [0,ff] joined by ':' characters.
+ * A valid byte array representation for a MacAddress is a non-null array of length 6.
+ *
+ * @param addr a byte array representation of a MAC address.
+ * @return the String representation of the MAC address.
+ * @throws IllegalArgumentException if the given byte array is not a valid representation.
+ *
+ * @hide
+ */
+ public static @NonNull String stringAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return null;
+ }
+ return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ }
+
+ private static byte[] byteAddrFromLongAddr(long addr) {
+ return MacAddressUtils.byteAddrFromLongAddr(addr);
+ }
+
+ private static long longAddrFromByteAddr(byte[] addr) {
+ return MacAddressUtils.longAddrFromByteAddr(addr);
+ }
+
+ // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
+ // that avoids the allocation of an intermediary byte[].
+ private static long longAddrFromStringAddr(String addr) {
+ Preconditions.checkNotNull(addr);
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ for (int i = 0; i < parts.length; i++) {
+ int x = Integer.valueOf(parts[i], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ longAddr = x + (longAddr << 8);
+ }
+ return longAddr;
+ }
+
+ // Internal conversion function equivalent to stringAddrFromByteAddr(byteAddrFromLongAddr(addr))
+ // that avoids the allocation of an intermediary byte[].
+ private static @NonNull String stringAddrFromLongAddr(long addr) {
+ return String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ (addr >> 40) & 0xff,
+ (addr >> 32) & 0xff,
+ (addr >> 24) & 0xff,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff);
+ }
+
+ /**
+ * Creates a MacAddress from the given String representation. A valid String representation
+ * for a MacAddress is a series of 6 values in the range [0,ff] printed in hexadecimal
+ * and joined by ':' characters.
+ *
+ * @param addr a String representation of a MAC address.
+ * @return the MacAddress corresponding to the given String representation.
+ * @throws IllegalArgumentException if the given String is not a valid representation.
+ */
+ public static @NonNull MacAddress fromString(@NonNull String addr) {
+ return new MacAddress(longAddrFromStringAddr(addr));
+ }
+
+ /**
+ * Creates a MacAddress from the given byte array representation.
+ * A valid byte array representation for a MacAddress is a non-null array of length 6.
+ *
+ * @param addr a byte array representation of a MAC address.
+ * @return the MacAddress corresponding to the given byte array representation.
+ * @throws IllegalArgumentException if the given byte array is not a valid representation.
+ */
+ public static @NonNull MacAddress fromBytes(@NonNull byte[] addr) {
+ return new MacAddress(longAddrFromByteAddr(addr));
+ }
+
+ /**
+ * Returns a generated MAC address whose 24 least significant bits constituting the
+ * NIC part of the address are randomly selected and has Google OUI base.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @return a random locally assigned, unicast MacAddress with Google OUI.
+ *
+ * @hide
+ */
+ public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
+ return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
+ }
+
+ // Convenience function for working around the lack of byte literals.
+ private static byte[] addr(int... in) {
+ if (in.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(Arrays.toString(in)
+ + " was not an array with length equal to " + ETHER_ADDR_LEN);
+ }
+ byte[] out = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ out[i] = (byte) in[i];
+ }
+ return out;
+ }
+
+ /**
+ * Checks if this MAC Address matches the provided range.
+ *
+ * @param baseAddress MacAddress representing the base address to compare with.
+ * @param mask MacAddress representing the mask to use during comparison.
+ * @return true if this MAC Address matches the given range.
+ *
+ */
+ public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
+ Preconditions.checkNotNull(baseAddress);
+ Preconditions.checkNotNull(mask);
+ return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr);
+ }
+
+ /**
+ * Create a link-local Inet6Address from the MAC address. The EUI-48 MAC address is converted
+ * to an EUI-64 MAC address per RFC 4291. The resulting EUI-64 is used to construct a link-local
+ * IPv6 address per RFC 4862.
+ *
+ * @return A link-local Inet6Address constructed from the MAC address.
+ */
+ public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() {
+ byte[] macEui48Bytes = toByteArray();
+ byte[] addr = new byte[16];
+
+ addr[0] = (byte) 0xfe;
+ addr[1] = (byte) 0x80;
+ addr[8] = (byte) (macEui48Bytes[0] ^ (byte) 0x02); // flip the link-local bit
+ addr[9] = macEui48Bytes[1];
+ addr[10] = macEui48Bytes[2];
+ addr[11] = (byte) 0xff;
+ addr[12] = (byte) 0xfe;
+ addr[13] = macEui48Bytes[3];
+ addr[14] = macEui48Bytes[4];
+ addr[15] = macEui48Bytes[5];
+
+ try {
+ return Inet6Address.getByAddress(null, addr, 0);
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+}
diff --git a/framework/src/android/net/NattKeepalivePacketData.java b/framework/src/android/net/NattKeepalivePacketData.java
new file mode 100644
index 0000000..c4f8fc2
--- /dev/null
+++ b/framework/src/android/net/NattKeepalivePacketData.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.system.OsConstants;
+
+import com.android.net.module.util.IpUtils;
+
+import java.net.Inet4Address;
+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
+ 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)
+ throws InvalidPacketException {
+
+ if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ if (dstPort != NattSocketKeepalive.NATT_PORT) {
+ throw new InvalidPacketException(ERROR_INVALID_PORT);
+ }
+
+ int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putShort((short) 0x4500); // IP version and TOS
+ buf.putShort((short) length);
+ buf.putInt(0); // ID, flags, offset
+ buf.put((byte) 64); // TTL
+ buf.put((byte) OsConstants.IPPROTO_UDP);
+ int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(srcAddress.getAddress());
+ buf.put(dstAddress.getAddress());
+ buf.putShort((short) srcPort);
+ buf.putShort((short) dstPort);
+ buf.putShort((short) (length - 20)); // UDP length
+ int udpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // UDP checksum
+ buf.put((byte) 0xff); // NAT-T keepalive
+ buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+ buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+
+ return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+ }
+
+ /** Parcelable Implementation */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel */
+ 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 @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR =
+ new Parcelable.Creator<NattKeepalivePacketData>() {
+ public NattKeepalivePacketData createFromParcel(Parcel in) {
+ final InetAddress srcAddress =
+ InetAddresses.parseNumericAddress(in.readString());
+ final InetAddress dstAddress =
+ InetAddresses.parseNumericAddress(in.readString());
+ final int srcPort = in.readInt();
+ final int dstPort = in.readInt();
+ try {
+ return NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort,
+ dstAddress, dstPort);
+ } catch (InvalidPacketException e) {
+ throw new IllegalArgumentException(
+ "Invalid NAT-T keepalive data: " + e.getError());
+ }
+ }
+
+ public NattKeepalivePacketData[] newArray(int size) {
+ 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/framework/src/android/net/NattSocketKeepalive.java b/framework/src/android/net/NattSocketKeepalive.java
new file mode 100644
index 0000000..a15d165
--- /dev/null
+++ b/framework/src/android/net/NattSocketKeepalive.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public final class NattSocketKeepalive extends SocketKeepalive {
+ /** The NAT-T destination port for IPsec */
+ public static final int NATT_PORT = 4500;
+
+ @NonNull private final InetAddress mSource;
+ @NonNull private final InetAddress mDestination;
+ private final int mResourceId;
+
+ NattSocketKeepalive(@NonNull IConnectivityManager service,
+ @NonNull Network network,
+ @NonNull ParcelFileDescriptor pfd,
+ int resourceId,
+ @NonNull InetAddress source,
+ @NonNull InetAddress destination,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ super(service, network, pfd, executor, callback);
+ mSource = source;
+ mDestination = destination;
+ mResourceId = resourceId;
+ }
+
+ @Override
+ void startImpl(int intervalSec) {
+ mExecutor.execute(() -> {
+ try {
+ mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId,
+ intervalSec, mCallback,
+ mSource.getHostAddress(), mDestination.getHostAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting socket keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ @Override
+ void stopImpl() {
+ mExecutor.execute(() -> {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping socket keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ }
+}
diff --git a/framework/src/android/net/Network.aidl b/framework/src/android/net/Network.aidl
new file mode 100644
index 0000000..0562202
--- /dev/null
+++ b/framework/src/android/net/Network.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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;
+
+@JavaOnlyStableParcelable parcelable Network;
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
new file mode 100644
index 0000000..b07bd68
--- /dev/null
+++ b/framework/src/android/net/Network.java
@@ -0,0 +1,535 @@
+/*
+ * 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.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.okhttp.internalandroidapi.Dns;
+import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.SocketFactory;
+
+/**
+ * Identifies a {@code Network}. This is supplied to applications via
+ * {@link ConnectivityManager.NetworkCallback} in response to the active
+ * {@link ConnectivityManager#requestNetwork} or passive
+ * {@link ConnectivityManager#registerNetworkCallback} calls.
+ * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis
+ * through a targeted {@link SocketFactory} or process-wide via
+ * {@link ConnectivityManager#bindProcessToNetwork}.
+ */
+public class Network implements Parcelable {
+
+ /**
+ * The unique id of the network.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public final int netId;
+
+ // Objects used to perform per-network operations such as getSocketFactory
+ // and openConnection, and a lock to protect access to them.
+ private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+ // mUrlConnectionFactory is initialized lazily when it is first needed.
+ @GuardedBy("mLock")
+ private HttpURLConnectionFactory mUrlConnectionFactory;
+ private final Object mLock = new Object();
+
+ // Default connection pool values. These are evaluated at startup, just
+ // like the OkHttp code. Also like the OkHttp code, we will throw parse
+ // exceptions at class loading time if the properties are set but are not
+ // valid integers.
+ private static final boolean httpKeepAlive =
+ Boolean.parseBoolean(System.getProperty("http.keepAlive", "true"));
+ private static final int httpMaxConnections =
+ httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
+ private static final long httpKeepAliveDurationMs =
+ Long.parseLong(System.getProperty("http.keepAliveDuration", "300000")); // 5 minutes.
+ // Value used to obfuscate network handle longs.
+ // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // value in the native/android/net.c NDK implementation.
+ private static final long HANDLE_MAGIC = 0xcafed00dL;
+ private static final int HANDLE_MAGIC_SIZE = 32;
+
+ // A boolean to control how getAllByName()/getByName() behaves in the face
+ // of Private DNS.
+ //
+ // When true, these calls will request that DNS resolution bypass any
+ // Private DNS that might otherwise apply. Use of this feature is restricted
+ // and permission checks are made by netd (attempts to bypass Private DNS
+ // without appropriate permission are silently turned into vanilla DNS
+ // requests). This only affects DNS queries made using this network object.
+ //
+ // It it not parceled to receivers because (a) it can be set or cleared at
+ // anytime and (b) receivers should be explicit about attempts to bypass
+ // Private DNS so that the intent of the code is easily determined and
+ // code search audits are possible.
+ private final transient boolean mPrivateDnsBypass;
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public Network(int netId) {
+ this(netId, false);
+ }
+
+ /**
+ * @hide
+ */
+ public Network(int netId, boolean privateDnsBypass) {
+ this.netId = netId;
+ this.mPrivateDnsBypass = privateDnsBypass;
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public Network(@NonNull Network that) {
+ this(that.netId, that.mPrivateDnsBypass);
+ }
+
+ /**
+ * Operates the same as {@code InetAddress.getAllByName} except that host
+ * resolution is done on this network.
+ *
+ * @param host the hostname or literal IP string to be resolved.
+ * @return the array of addresses associated with the specified host.
+ * @throws UnknownHostException if the address lookup fails.
+ */
+ public InetAddress[] getAllByName(String host) throws UnknownHostException {
+ return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
+ }
+
+ /**
+ * Operates the same as {@code InetAddress.getByName} except that host
+ * resolution is done on this network.
+ *
+ * @param host the hostname to be resolved to an address or {@code null}.
+ * @return the {@code InetAddress} instance representing the host.
+ * @throws UnknownHostException
+ * if the address lookup fails.
+ */
+ public InetAddress getByName(String host) throws UnknownHostException {
+ return InetAddress.getByNameOnNet(host, getNetIdForResolv());
+ }
+
+ /**
+ * Obtain a Network object for which Private DNS is to be bypassed when attempting
+ * to use {@link #getAllByName(String)}/{@link #getByName(String)} methods on the given
+ * instance for hostname resolution.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Network getPrivateDnsBypassingCopy() {
+ return new Network(netId, true);
+ }
+
+ /**
+ * Get the unique id of the network.
+ *
+ * @hide
+ */
+ @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
+ * in system/netd/include/NetdClient.h.
+ *
+ * @hide
+ */
+ public int getNetIdForResolv() {
+ return mPrivateDnsBypass
+ ? (int) (0x80000000L | (long) netId) // Non-portable DNS resolution flag.
+ : netId;
+ }
+
+ /**
+ * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+ */
+ private class NetworkBoundSocketFactory extends SocketFactory {
+ private Socket connectToHost(String host, int port, SocketAddress localAddress)
+ throws IOException {
+ // Lookup addresses only on this Network.
+ InetAddress[] hostAddresses = getAllByName(host);
+ // Try all addresses.
+ for (int i = 0; i < hostAddresses.length; i++) {
+ try {
+ Socket socket = createSocket();
+ boolean failed = true;
+ try {
+ if (localAddress != null) socket.bind(localAddress);
+ socket.connect(new InetSocketAddress(hostAddresses[i], port));
+ failed = false;
+ return socket;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
+ } catch (IOException e) {
+ if (i == (hostAddresses.length - 1)) throw e;
+ }
+ }
+ throw new UnknownHostException(host);
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
+ throws IOException {
+ return connectToHost(host, port, new InetSocketAddress(localHost, localPort));
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+ int localPort) throws IOException {
+ Socket socket = createSocket();
+ boolean failed = true;
+ try {
+ socket.bind(new InetSocketAddress(localAddress, localPort));
+ socket.connect(new InetSocketAddress(address, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ Socket socket = createSocket();
+ boolean failed = true;
+ try {
+ socket.connect(new InetSocketAddress(host, port));
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
+ return socket;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+ return connectToHost(host, port, null);
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ Socket socket = new Socket();
+ boolean failed = true;
+ try {
+ bindSocket(socket);
+ failed = false;
+ } finally {
+ if (failed) IoUtils.closeQuietly(socket);
+ }
+ return socket;
+ }
+ }
+
+ /**
+ * Returns a {@link SocketFactory} bound to this network. Any {@link Socket} created by
+ * this factory will have its traffic sent over this {@code Network}. Note that if this
+ * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
+ * past or future will cease to work.
+ *
+ * @return a {@link SocketFactory} which produces {@link Socket} instances bound to this
+ * {@code Network}.
+ */
+ public SocketFactory getSocketFactory() {
+ if (mNetworkBoundSocketFactory == null) {
+ synchronized (mLock) {
+ if (mNetworkBoundSocketFactory == null) {
+ mNetworkBoundSocketFactory = new NetworkBoundSocketFactory();
+ }
+ }
+ }
+ return mNetworkBoundSocketFactory;
+ }
+
+ private static HttpURLConnectionFactory createUrlConnectionFactory(Dns dnsLookup) {
+ // Set configuration on the HttpURLConnectionFactory that will be good for all
+ // connections created by this Network. Configuration that might vary is left
+ // until openConnection() and passed as arguments.
+ HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+ urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
+ // A private connection pool just for this Network.
+ urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
+ httpKeepAliveDurationMs, TimeUnit.MILLISECONDS);
+ return urlConnectionFactory;
+ }
+
+ /**
+ * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent
+ * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}.
+ *
+ * @return a {@code URLConnection} to the resource referred to by this URL.
+ * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS.
+ * @throws IOException if an error occurs while opening the connection.
+ * @see java.net.URL#openConnection()
+ */
+ public URLConnection openConnection(URL url) throws IOException {
+ final ConnectivityManager cm = ConnectivityManager.getInstanceOrNull();
+ if (cm == null) {
+ throw new IOException("No ConnectivityManager yet constructed, please construct one");
+ }
+ // TODO: Should this be optimized to avoid fetching the global proxy for every request?
+ final ProxyInfo proxyInfo = cm.getProxyForNetwork(this);
+ final java.net.Proxy proxy;
+ if (proxyInfo != null) {
+ proxy = proxyInfo.makeProxy();
+ } else {
+ proxy = java.net.Proxy.NO_PROXY;
+ }
+ return openConnection(url, proxy);
+ }
+
+ /**
+ * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent
+ * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}.
+ *
+ * @param proxy the proxy through which the connection will be established.
+ * @return a {@code URLConnection} to the resource referred to by this URL.
+ * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS.
+ * @throws IllegalArgumentException if the argument proxy is null.
+ * @throws IOException if an error occurs while opening the connection.
+ * @see java.net.URL#openConnection()
+ */
+ public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
+ if (proxy == null) throw new IllegalArgumentException("proxy is null");
+ // TODO: This creates a connection pool and host resolver for
+ // every Network object, instead of one for every NetId. This is
+ // suboptimal, because an app could potentially have more than one
+ // Network object for the same NetId, causing increased memory footprint
+ // and performance penalties due to lack of connection reuse (connection
+ // setup time, congestion window growth time, etc.).
+ //
+ // Instead, investigate only having one connection pool and host resolver
+ // for every NetId, perhaps by using a static HashMap of NetIds to
+ // connection pools and host resolvers. The tricky part is deciding when
+ // to remove a map entry; a WeakHashMap shouldn't be used because whether
+ // a Network is referenced doesn't correlate with whether a new Network
+ // will be instantiated in the near future with the same NetID. A good
+ // solution would involve purging empty (or when all connections are timed
+ // out) ConnectionPools.
+ final HttpURLConnectionFactory urlConnectionFactory;
+ synchronized (mLock) {
+ if (mUrlConnectionFactory == null) {
+ Dns dnsLookup = hostname -> Arrays.asList(getAllByName(hostname));
+ mUrlConnectionFactory = createUrlConnectionFactory(dnsLookup);
+ }
+ urlConnectionFactory = mUrlConnectionFactory;
+ }
+ SocketFactory socketFactory = getSocketFactory();
+ return urlConnectionFactory.openConnection(url, socketFactory, proxy);
+ }
+
+ /**
+ * Binds the specified {@link DatagramSocket} to this {@code Network}. All data traffic on the
+ * socket will be sent on this {@code Network}, irrespective of any process-wide network binding
+ * set by {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be
+ * connected.
+ */
+ public void bindSocket(DatagramSocket socket) throws IOException {
+ // Query a property of the underlying socket to ensure that the socket's file descriptor
+ // exists, is available to bind to a network and is not closed.
+ socket.getReuseAddress();
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
+ }
+
+ /**
+ * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
+ * will be sent on this {@code Network}, irrespective of any process-wide network binding set by
+ * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
+ */
+ public void bindSocket(Socket socket) throws IOException {
+ // Query a property of the underlying socket to ensure that the socket's file descriptor
+ // exists, is available to bind to a network and is not closed.
+ socket.getReuseAddress();
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
+ }
+
+ /**
+ * Binds the specified {@link FileDescriptor} to this {@code Network}. All data traffic on the
+ * socket represented by this file descriptor will be sent on this {@code Network},
+ * irrespective of any process-wide network binding set by
+ * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
+ */
+ public void bindSocket(FileDescriptor fd) throws IOException {
+ try {
+ final SocketAddress peer = Os.getpeername(fd);
+ final InetAddress inetPeer = ((InetSocketAddress) peer).getAddress();
+ if (!inetPeer.isAnyLocalAddress()) {
+ // Apparently, the kernel doesn't update a connected UDP socket's
+ // routing upon mark changes.
+ throw new SocketException("Socket is connected");
+ }
+ } catch (ErrnoException e) {
+ // getpeername() failed.
+ if (e.errno != OsConstants.ENOTCONN) {
+ throw e.rethrowAsSocketException();
+ }
+ } catch (ClassCastException e) {
+ // Wasn't an InetSocketAddress.
+ throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
+ }
+
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
+ if (err != 0) {
+ // bindSocketToNetwork returns negative errno.
+ throw new ErrnoException("Binding socket to network " + netId, -err)
+ .rethrowAsSocketException();
+ }
+ }
+
+ /**
+ * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}.
+ *
+ * @param networkHandle a handle returned from {@link #getNetworkHandle}.
+ * @return A {@link Network} object derived from {@code networkHandle}.
+ */
+ public static Network fromNetworkHandle(long networkHandle) {
+ if (networkHandle == 0) {
+ throw new IllegalArgumentException(
+ "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
+ }
+ if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
+ || networkHandle < 0) {
+ throw new IllegalArgumentException(
+ "Value passed to fromNetworkHandle() is not a network handle.");
+ }
+ return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+ }
+
+ /**
+ * Returns a handle representing this {@code Network}, for use with the NDK API.
+ */
+ public long getNetworkHandle() {
+ // The network handle is explicitly not the same as the netId.
+ //
+ // The netId is an implementation detail which might be changed in the
+ // future, or which alone (i.e. in the absence of some additional
+ // context) might not be sufficient to fully identify a Network.
+ //
+ // As such, the intention is to prevent accidental misuse of the API
+ // that might result if a developer assumed that handles and netIds
+ // were identical and passing a netId to a call expecting a handle
+ // "just worked". Such accidental misuse, if widely deployed, might
+ // prevent future changes to the semantics of the netId field or
+ // inhibit the expansion of state required for Network objects.
+ //
+ // This extra layer of indirection might be seen as paranoia, and might
+ // never end up being necessary, but the added complexity is trivial.
+ // At some future date it may be desirable to realign the handle with
+ // Multiple Provisioning Domains API recommendations, as made by the
+ // IETF mif working group.
+ if (netId == 0) {
+ return 0L; // make this zero condition obvious for debugging
+ }
+ return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(netId);
+ }
+
+ public static final @android.annotation.NonNull Creator<Network> CREATOR =
+ new Creator<Network>() {
+ public Network createFromParcel(Parcel in) {
+ int netId = in.readInt();
+
+ return new Network(netId);
+ }
+
+ public Network[] newArray(int size) {
+ return new Network[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Network)) return false;
+ Network other = (Network)obj;
+ return this.netId == other.netId;
+ }
+
+ @Override
+ public int hashCode() {
+ return netId * 11;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(netId);
+ }
+
+ /** @hide */
+ 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/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
new file mode 100644
index 0000000..d22d82d
--- /dev/null
+++ b/framework/src/android/net/NetworkAgent.java
@@ -0,0 +1,1185 @@
+/*
+ * 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.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.RemoteException;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.util.Log;
+
+import com.android.connectivity.aidl.INetworkAgent;
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+import com.android.internal.annotations.VisibleForTesting;
+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.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 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 should 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
+ */
+@SystemApi
+public abstract class NetworkAgent {
+ /**
+ * The {@link Network} corresponding to this object.
+ */
+ @Nullable
+ private volatile Network mNetwork;
+
+ @Nullable
+ private volatile INetworkAgentRegistry mRegistry;
+
+ private interface RegistryAction {
+ void execute(@NonNull INetworkAgentRegistry registry) throws RemoteException;
+ }
+
+ private final Handler mHandler;
+ private final String LOG_TAG;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+ private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>();
+ private volatile long mLastBwRefreshTime = 0;
+ private static final long BW_REFRESH_MIN_WIN_MS = 500;
+ private boolean mBandwidthUpdateScheduled = false;
+ private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+ @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;
+
+ /**
+ * 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;
+
+ /**
+ * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
+ * 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;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * NetworkCapabilties.
+ * obj = NetworkCapabilities
+ * @hide
+ */
+ public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;
+
+ /**
+ * 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 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.
+ * arg1 = network score int
+ * @hide
+ */
+ public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * list of underlying networks.
+ * obj = array of Network objects
+ * @hide
+ */
+ public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
+ * networks status - whether we could use the network or could not, due to
+ * either a bad network configuration (no internet link) or captive portal.
+ *
+ * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+ * 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 final String REDIRECT_URL_KEY = "redirect URL";
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to indicate this network was
+ * explicitly selected. This should be sent before the NetworkInfo is marked
+ * 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;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of
+ * whether the network should in the future be used even if not validated.
+ * This decision is made by the user, but it is the network transport's
+ * 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;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
+ * periodically on the given interval.
+ *
+ * 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 = 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;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to provide status on a socket keepalive
+ * request. This may either be the reply to a CMD_START_SOCKET_KEEPALIVE, or an asynchronous
+ * error notification.
+ *
+ * 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 = hardware slot number of the keepalive
+ * arg2 = error code
+ * @hide
+ */
+ public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * 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;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to complete the bidirectional connection.
+ * obj = INetworkAgentRegistry
+ */
+ private static final int EVENT_AGENT_CONNECTED = BASE + 18;
+
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent that it was disconnected.
+ */
+ private static final int EVENT_AGENT_DISCONNECTED = BASE + 19;
+
+ /**
+ * Sent by QosCallbackTracker to {@link NetworkAgent} to register a new filter with
+ * callback.
+ *
+ * arg1 = QoS agent callback ID
+ * obj = {@link QosFilter}
+ * @hide
+ */
+ public static final int CMD_REGISTER_QOS_CALLBACK = BASE + 20;
+
+ /**
+ * Sent by QosCallbackTracker to {@link NetworkAgent} to unregister a callback.
+ *
+ * arg1 = QoS agent callback ID
+ * @hide
+ */
+ public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
+
+ private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
+ // The subtype can be changed with (TODO) setLegacySubtype, but it starts
+ // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
+ final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, "");
+ ni.setIsAvailable(true);
+ ni.setDetailedState(NetworkInfo.DetailedState.CONNECTING, null /* reason */,
+ 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));
+ }
+
+ 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) {
+ mHandler = new NetworkAgentHandler(looper);
+ LOG_TAG = logTag;
+ mNetworkInfo = new NetworkInfo(ni);
+ this.providerId = providerId;
+ if (ni == null || nc == null || lp == null) {
+ throw new IllegalArgumentException();
+ }
+
+ mInitialConfiguration = new InitialConfiguration(context,
+ new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true),
+ new LinkProperties(lp), score, config, ni);
+ }
+
+ private class NetworkAgentHandler extends Handler {
+ NetworkAgentHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AGENT_CONNECTED: {
+ if (mRegistry != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG) log("NetworkAgent fully connected");
+ synchronized (mPreConnectedQueue) {
+ final INetworkAgentRegistry registry = (INetworkAgentRegistry) msg.obj;
+ mRegistry = registry;
+ for (RegistryAction a : mPreConnectedQueue) {
+ try {
+ a.execute(registry);
+ } catch (RemoteException e) {
+ Log.wtf(LOG_TAG, "Communication error with registry", e);
+ // Fall through
+ }
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
+ }
+ case EVENT_AGENT_DISCONNECTED: {
+ if (DBG) log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+ onNetworkUnwanted();
+ synchronized (mPreConnectedQueue) {
+ mRegistry = 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: {
+ onSignalStrengthThresholdsUpdated((int[]) msg.obj);
+ 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;
+ }
+ case CMD_REGISTER_QOS_CALLBACK: {
+ onQosCallbackRegistered(
+ msg.arg1 /* QoS callback id */,
+ (QosFilter) msg.obj /* QoS filter */);
+ break;
+ }
+ case CMD_UNREGISTER_QOS_CALLBACK: {
+ onQosCallbackUnregistered(
+ msg.arg1 /* QoS callback id */);
+ 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 NetworkAgentBinder(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;
+ }
+
+ private static class NetworkAgentBinder extends INetworkAgent.Stub {
+ private static final String LOG_TAG = NetworkAgentBinder.class.getSimpleName();
+
+ private final Handler mHandler;
+
+ private NetworkAgentBinder(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void onRegistered(@NonNull INetworkAgentRegistry registry) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_CONNECTED, registry));
+ }
+
+ @Override
+ public void onDisconnected() {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_AGENT_DISCONNECTED));
+ }
+
+ @Override
+ public void onBandwidthUpdateRequested() {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_REQUEST_BANDWIDTH_UPDATE));
+ }
+
+ @Override
+ public void onValidationStatusChanged(
+ int validationStatus, @Nullable String captivePortalUrl) {
+ // TODO: consider using a parcelable as argument when the interface is structured
+ Bundle redirectUrlBundle = new Bundle();
+ redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, captivePortalUrl);
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_REPORT_NETWORK_STATUS,
+ validationStatus, 0, redirectUrlBundle));
+ }
+
+ @Override
+ public void onSaveAcceptUnvalidated(boolean acceptUnvalidated) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_SAVE_ACCEPT_UNVALIDATED,
+ acceptUnvalidated ? 1 : 0, 0));
+ }
+
+ @Override
+ public void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ @NonNull NattKeepalivePacketData packetData) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE,
+ slot, intervalDurationMs, packetData));
+ }
+
+ @Override
+ public void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ @NonNull TcpKeepalivePacketData packetData) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE,
+ slot, intervalDurationMs, packetData));
+ }
+
+ @Override
+ public void onStopSocketKeepalive(int slot) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_STOP_SOCKET_KEEPALIVE, slot, 0));
+ }
+
+ @Override
+ public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ CMD_SET_SIGNAL_STRENGTH_THRESHOLDS, thresholds));
+ }
+
+ @Override
+ public void onPreventAutomaticReconnect() {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_PREVENT_AUTOMATIC_RECONNECT));
+ }
+
+ @Override
+ public void onAddNattKeepalivePacketFilter(int slot,
+ @NonNull NattKeepalivePacketData packetData) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER,
+ slot, 0, packetData));
+ }
+
+ @Override
+ public void onAddTcpKeepalivePacketFilter(int slot,
+ @NonNull TcpKeepalivePacketData packetData) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER,
+ slot, 0, packetData));
+ }
+
+ @Override
+ public void onRemoveKeepalivePacketFilter(int slot) {
+ mHandler.sendMessage(mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
+ slot, 0));
+ }
+
+ @Override
+ public void onQosFilterCallbackRegistered(final int qosCallbackId,
+ final QosFilterParcelable qosFilterParcelable) {
+ if (qosFilterParcelable.getQosFilter() != null) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(CMD_REGISTER_QOS_CALLBACK, qosCallbackId, 0,
+ qosFilterParcelable.getQosFilter()));
+ return;
+ }
+
+ Log.wtf(LOG_TAG, "onQosFilterCallbackRegistered: qos filter is null.");
+ }
+
+ @Override
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
+ }
+ }
+
+ /**
+ * 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 INetworkAgent registerForTest(final Network network) {
+ log("Registering NetworkAgent for test");
+ synchronized (mRegisterLock) {
+ mNetwork = network;
+ mInitialConfiguration = null;
+ }
+ return new NetworkAgentBinder(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(@NonNull RegistryAction action) {
+ synchronized (mPreConnectedQueue) {
+ if (mRegistry != null) {
+ try {
+ action.execute(mRegistry);
+ } catch (RemoteException e) {
+ Log.wtf(LOG_TAG, "Error executing registry action", e);
+ // Fall through: the channel is asynchronous and does not report errors back
+ }
+ } else {
+ mPreConnectedQueue.add(action);
+ }
+ }
+ }
+
+ /**
+ * Must be called by the agent when the network's {@link LinkProperties} change.
+ * @param linkProperties the new LinkProperties.
+ */
+ public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+ Objects.requireNonNull(linkProperties);
+ final LinkProperties lp = new LinkProperties(linkProperties);
+ queueOrSendMessage(reg -> reg.sendLinkProperties(lp));
+ }
+
+ /**
+ * Must be called by the agent when the network's underlying networks change.
+ *
+ * <p>{@code networks} is one of the following:
+ * <ul>
+ * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
+ * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
+ * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
+ * first in the array.</li>
+ * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
+ * underlying network connection, and thus, app traffic will not be sent or received.</li>
+ * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
+ * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
+ * APIs mentioned above to send traffic over specific channels.</li>
+ * </ul>
+ *
+ * @param underlyingNetworks the new list of underlying networks.
+ * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
+ */
+ public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+ final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
+ ? new ArrayList<>(underlyingNetworks) : null;
+ queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
+ }
+
+ /**
+ * Inform ConnectivityService that this agent has now connected.
+ * Call {@link #unregister} to disconnect.
+ */
+ public void markConnected() {
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
+ mNetworkInfo.getExtraInfo());
+ queueOrSendNetworkInfo(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() {
+ // When unregistering an agent nobody should use the extrainfo (or reason) any more.
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
+ null /* extraInfo */);
+ queueOrSendNetworkInfo(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) {
+ mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
+ queueOrSendNetworkInfo(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) {
+ mNetworkInfo.setExtraInfo(extraInfo);
+ queueOrSendNetworkInfo(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 final void sendNetworkInfo(NetworkInfo networkInfo) {
+ queueOrSendNetworkInfo(new NetworkInfo(networkInfo));
+ }
+
+ private void queueOrSendNetworkInfo(NetworkInfo networkInfo) {
+ queueOrSendMessage(reg -> reg.sendNetworkInfo(networkInfo));
+ }
+
+ /**
+ * Must be called by the agent when the network's {@link NetworkCapabilities} change.
+ * @param networkCapabilities the new NetworkCapabilities.
+ */
+ public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ Objects.requireNonNull(networkCapabilities);
+ mBandwidthUpdatePending.set(false);
+ mLastBwRefreshTime = System.currentTimeMillis();
+ final NetworkCapabilities nc =
+ new NetworkCapabilities(networkCapabilities,
+ /* parcelLocationSensitiveFields */ true);
+ queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
+ }
+
+ /**
+ * Must be called by the agent to update the score of this network.
+ *
+ * @param score the new score, between 0 and 99.
+ */
+ public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
+ if (score < 0) {
+ throw new IllegalArgumentException("Score must be >= 0");
+ }
+ queueOrSendMessage(reg -> reg.sendScore(score));
+ }
+
+ /**
+ * 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
+ * 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.
+ * @hide should move to NetworkAgentConfig.
+ */
+ public void explicitlySelected(boolean acceptUnvalidated) {
+ explicitlySelected(true /* explicitlySelected */, acceptUnvalidated);
+ }
+
+ /**
+ * 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(reg -> reg.sendExplicitlySelected(
+ explicitlySelected, acceptUnvalidated));
+ }
+
+ /**
+ * Called when ConnectivityService has indicated they no longer want this network.
+ * The parent factory should (previously) have received indication of the change
+ * as well, either canceling NetworkRequests or altering their score such that this
+ * network won't be immediately requested again.
+ */
+ 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.
+ *
+ * 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 VALIDATION_STATUS_VALID} if Internet connectivity was validated,
+ * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
+ *
+ * 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 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 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);
+ }
+
+ /**
+ * 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(reg -> reg.sendSocketKeepaliveEvent(slot, event));
+ }
+ /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
+ public void onSocketKeepaliveEvent(int slot, int reason) {
+ sendSocketKeepaliveEvent(slot, reason);
+ }
+
+ /**
+ * Called by ConnectivityService to add specific packet filter to network hardware to block
+ * 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) {
+ }
+
+ /**
+ * 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 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) {
+ }
+
+ /**
+ * Called when the user asks to not stay connected to this network because it was found to not
+ * provide Internet access. Usually followed by call to {@code unwanted}. The transport is
+ * 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() {
+ }
+
+ /**
+ * Called when a qos callback is registered with a filter.
+ * @param qosCallbackId the id for the callback registered
+ * @param filter the filter being registered
+ */
+ public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+ }
+
+ /**
+ * Called when a qos callback is registered with a filter.
+ * <p/>
+ * Any QoS events that are sent with the same callback id after this method is called
+ * are a no-op.
+ *
+ * @param qosCallbackId the id for the callback being unregistered
+ */
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ }
+
+
+ /**
+ * Sends the attributes of Eps Bearer Qos Session back to the Application
+ *
+ * @param qosCallbackId the callback id that the session belongs to
+ * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+ * @param attributes the attributes of the Eps Qos Session
+ */
+ public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
+ @NonNull final EpsBearerQosSessionAttributes attributes) {
+ Objects.requireNonNull(attributes, "The attributes must be non-null");
+ queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+ new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
+ attributes));
+ }
+
+ /**
+ * Sends event that the Eps Qos Session was lost.
+ *
+ * @param qosCallbackId the callback id that the session belongs to
+ * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+ */
+ public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
+ queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
+ new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
+ }
+
+ /**
+ * Sends the exception type back to the application.
+ *
+ * The NetworkAgent should not send anymore messages with this id.
+ *
+ * @param qosCallbackId the callback id this exception belongs to
+ * @param exceptionType the type of exception
+ */
+ public final void sendQosCallbackError(final int qosCallbackId,
+ @QosCallbackException.ExceptionType final int exceptionType) {
+ queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
+ }
+
+
+ /** @hide */
+ protected void log(final String s) {
+ Log.d(LOG_TAG, "NetworkAgent: " + s);
+ }
+}
diff --git a/framework/src/android/net/NetworkAgentConfig.aidl b/framework/src/android/net/NetworkAgentConfig.aidl
new file mode 100644
index 0000000..cb70bdd
--- /dev/null
+++ b/framework/src/android/net/NetworkAgentConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable NetworkAgentConfig;
diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java
new file mode 100644
index 0000000..664c265
--- /dev/null
+++ b/framework/src/android/net/NetworkAgentConfig.java
@@ -0,0 +1,440 @@
+/*
+ * 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 static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+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
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @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
+ @SystemApi(client = MODULE_LIBRARIES)
+ 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/framework/src/android/net/NetworkCapabilities.aidl b/framework/src/android/net/NetworkCapabilities.aidl
new file mode 100644
index 0000000..01d3286
--- /dev/null
+++ b/framework/src/android/net/NetworkCapabilities.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** 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;
+
+@JavaOnlyStableParcelable parcelable NetworkCapabilities;
+
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
new file mode 100644
index 0000000..3843b9a
--- /dev/null
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -0,0 +1,2517 @@
+/*
+ * 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 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.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;
+
+/**
+ * Representation of the capabilities of an active network. Instances are
+ * typically obtained through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)}
+ * or {@link ConnectivityManager#getNetworkCapabilities(Network)}.
+ * <p>
+ * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of
+ * network selection. Rather than indicate a need for Wi-Fi because an
+ * application needs high bandwidth and risk obsolescence when a new, fast
+ * network appears (like LTE), the application should specify it needs high
+ * bandwidth. Similarly if an application needs an unmetered network for a bulk
+ * transfer it can specify that rather than assuming all cellular based
+ * connections are metered and all Wi-Fi based connections are not.
+ */
+public final class NetworkCapabilities implements Parcelable {
+ private static final String TAG = "NetworkCapabilities";
+
+ // Set to true when private DNS is broken.
+ private boolean mPrivateDnsBroken;
+
+ /**
+ * Uid of the app making the request.
+ */
+ private int mRequestorUid;
+
+ /**
+ * Package name of the app making the request.
+ */
+ private String mRequestorPackageName;
+
+ /**
+ * Indicates whether parceling should preserve fields that are set based on permissions of
+ * the process receiving the {@link NetworkCapabilities}.
+ */
+ private final boolean mParcelLocationSensitiveFields;
+
+ public NetworkCapabilities() {
+ mParcelLocationSensitiveFields = false;
+ clearAll();
+ mNetworkCapabilities = DEFAULT_CAPABILITIES;
+ }
+
+ public NetworkCapabilities(NetworkCapabilities nc) {
+ this(nc, false /* parcelLocationSensitiveFields */);
+ }
+
+ /**
+ * Make a copy of NetworkCapabilities.
+ *
+ * @param nc Original NetworkCapabilities
+ * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not.
+ * @hide
+ */
+ @SystemApi
+ public NetworkCapabilities(
+ @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) {
+ mParcelLocationSensitiveFields = parcelLocationSensitiveFields;
+ if (nc != null) {
+ set(nc);
+ }
+ }
+
+ /**
+ * Completely clears the contents of this object, removing even the capabilities that are set
+ * by default when the object is constructed.
+ * @hide
+ */
+ public void clearAll() {
+ // Ensures that the internal copies maintained by the connectivity stack does not set
+ // this bit.
+ if (mParcelLocationSensitiveFields) {
+ throw new UnsupportedOperationException(
+ "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set");
+ }
+ mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
+ mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+ mNetworkSpecifier = null;
+ mTransportInfo = null;
+ mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
+ mUids = null;
+ mAdministratorUids = new int[0];
+ mOwnerUid = Process.INVALID_UID;
+ mSSID = null;
+ mPrivateDnsBroken = false;
+ mRequestorUid = Process.INVALID_UID;
+ mRequestorPackageName = null;
+ }
+
+ /**
+ * Set all contents of this object to the contents of a NetworkCapabilities.
+ *
+ * @param nc Original NetworkCapabilities
+ * @hide
+ */
+ public void set(@NonNull NetworkCapabilities nc) {
+ mNetworkCapabilities = nc.mNetworkCapabilities;
+ mTransportTypes = nc.mTransportTypes;
+ mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
+ mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
+ mNetworkSpecifier = nc.mNetworkSpecifier;
+ if (nc.getTransportInfo() != null) {
+ setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields));
+ } else {
+ setTransportInfo(null);
+ }
+ mSignalStrength = nc.mSignalStrength;
+ setUids(nc.mUids); // Will make the defensive copy
+ setAdministratorUids(nc.getAdministratorUids());
+ mOwnerUid = nc.mOwnerUid;
+ mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
+ mSSID = nc.mSSID;
+ mPrivateDnsBroken = nc.mPrivateDnsBroken;
+ mRequestorUid = nc.mRequestorUid;
+ mRequestorPackageName = nc.mRequestorPackageName;
+ }
+
+ /**
+ * Represents the network's capabilities. If any are specified they will be satisfied
+ * by any Network that matches all of them.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private long mNetworkCapabilities;
+
+ /**
+ * If any capabilities specified here they must not exist in the matching Network.
+ */
+ private long mUnwantedNetworkCapabilities;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
+ NET_CAPABILITY_MMS,
+ NET_CAPABILITY_SUPL,
+ NET_CAPABILITY_DUN,
+ NET_CAPABILITY_FOTA,
+ NET_CAPABILITY_IMS,
+ NET_CAPABILITY_CBS,
+ NET_CAPABILITY_WIFI_P2P,
+ NET_CAPABILITY_IA,
+ NET_CAPABILITY_RCS,
+ NET_CAPABILITY_XCAP,
+ NET_CAPABILITY_EIMS,
+ NET_CAPABILITY_NOT_METERED,
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_RESTRICTED,
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VPN,
+ NET_CAPABILITY_VALIDATED,
+ NET_CAPABILITY_CAPTIVE_PORTAL,
+ NET_CAPABILITY_NOT_ROAMING,
+ NET_CAPABILITY_FOREGROUND,
+ NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_SUSPENDED,
+ NET_CAPABILITY_OEM_PAID,
+ NET_CAPABILITY_MCX,
+ NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NET_CAPABILITY_OEM_PRIVATE,
+ NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
+ })
+ public @interface NetCapability { }
+
+ /**
+ * Indicates this is a network that has the ability to reach the
+ * carrier's MMSC for sending and receiving MMS messages.
+ */
+ public static final int NET_CAPABILITY_MMS = 0;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * SUPL server, used to retrieve GPS information.
+ */
+ public static final int NET_CAPABILITY_SUPL = 1;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * DUN or tethering gateway.
+ */
+ public static final int NET_CAPABILITY_DUN = 2;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * FOTA portal, used for over the air updates.
+ */
+ public static final int NET_CAPABILITY_FOTA = 3;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * IMS servers, used for network registration and signaling.
+ */
+ public static final int NET_CAPABILITY_IMS = 4;
+
+ /**
+ * Indicates this is a network that has the ability to reach the carrier's
+ * CBS servers, used for carrier specific services.
+ */
+ public static final int NET_CAPABILITY_CBS = 5;
+
+ /**
+ * Indicates this is a network that has the ability to reach a Wi-Fi direct
+ * peer.
+ */
+ public static final int NET_CAPABILITY_WIFI_P2P = 6;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * Initial Attach servers.
+ */
+ public static final int NET_CAPABILITY_IA = 7;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * RCS servers, used for Rich Communication Services.
+ */
+ public static final int NET_CAPABILITY_RCS = 8;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * XCAP servers, used for configuration and control.
+ */
+ public static final int NET_CAPABILITY_XCAP = 9;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's
+ * Emergency IMS servers or other services, used for network signaling
+ * during emergency calls.
+ */
+ public static final int NET_CAPABILITY_EIMS = 10;
+
+ /**
+ * Indicates that this network is unmetered.
+ */
+ public static final int NET_CAPABILITY_NOT_METERED = 11;
+
+ /**
+ * Indicates that this network should be able to reach the internet.
+ */
+ public static final int NET_CAPABILITY_INTERNET = 12;
+
+ /**
+ * Indicates that this network is available for general use. If this is not set
+ * applications should not attempt to communicate on this network. Note that this
+ * is simply informative and not enforcement - enforcement is handled via other means.
+ * Set by default.
+ */
+ public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
+
+ /**
+ * Indicates that the user has indicated implicit trust of this network. This
+ * generally means it's a sim-selected carrier, a plugged in ethernet, a paired
+ * BT device or a wifi the user asked to connect to. Untrusted networks
+ * are probably limited to unknown wifi AP. Set by default.
+ */
+ public static final int NET_CAPABILITY_TRUSTED = 14;
+
+ /**
+ * Indicates that this network is not a VPN. This capability is set by default and should be
+ * explicitly cleared for VPN networks.
+ */
+ public static final int NET_CAPABILITY_NOT_VPN = 15;
+
+ /**
+ * Indicates that connectivity on this network was successfully validated. For example, for a
+ * network with NET_CAPABILITY_INTERNET, it means that Internet connectivity was successfully
+ * detected.
+ */
+ public static final int NET_CAPABILITY_VALIDATED = 16;
+
+ /**
+ * Indicates that this network was found to have a captive portal in place last time it was
+ * probed.
+ */
+ public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
+
+ /**
+ * Indicates that this network is not roaming.
+ */
+ public static final int NET_CAPABILITY_NOT_ROAMING = 18;
+
+ /**
+ * Indicates that this network is available for use by apps, and not a network that is being
+ * kept up in the background to facilitate fast network switching.
+ */
+ public static final int NET_CAPABILITY_FOREGROUND = 19;
+
+ /**
+ * Indicates that this network is not congested.
+ * <p>
+ * When a network is congested, applications should defer network traffic
+ * that can be done at a later time, such as uploading analytics.
+ */
+ public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+
+ /**
+ * Indicates that this network is not currently suspended.
+ * <p>
+ * When a network is suspended, the network's IP addresses and any connections
+ * established on the network remain valid, but the network is temporarily unable
+ * to transfer data. This can happen, for example, if a cellular network experiences
+ * a temporary loss of signal, such as when driving through a tunnel, etc.
+ * A network with this capability is not suspended, so is expected to be able to
+ * transfer data.
+ */
+ public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
+ /**
+ * Indicates that traffic that goes through this network is paid by oem. For example,
+ * this network can be used by system apps to upload telemetry data.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_OEM_PAID = 22;
+
+ /**
+ * Indicates this is a network that has the ability to reach a carrier's Mission Critical
+ * servers.
+ */
+ public static final int NET_CAPABILITY_MCX = 23;
+
+ /**
+ * Indicates that this network was tested to only provide partial connectivity.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
+
+ /**
+ * Indicates that this network is temporarily unmetered.
+ * <p>
+ * 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.
+ * Note that most apps should use {@link #NET_CAPABILITY_NOT_METERED} instead. For more
+ * information, see https://developer.android.com/about/versions/11/features/5g#meteredness.
+ */
+ public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+
+ /**
+ * Indicates that this network is private to the OEM and meant only for OEM use.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_OEM_PRIVATE = 26;
+
+ /**
+ * Indicates this is an internal vehicle network, meant to communicate with other
+ * automotive systems.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+
+ /**
+ * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+ *
+ * TODO(b/177299683): Add additional clarifying javadoc.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
+ private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
+
+ /**
+ * Network capabilities that are expected to be mutable, i.e., can change while a particular
+ * network is connected.
+ */
+ private static final long MUTABLE_CAPABILITIES =
+ // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
+ // http://b/18206275
+ (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_VALIDATED)
+ | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_FOREGROUND)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ /**
+ * Network capabilities that are not allowed in NetworkRequests. This exists because the
+ * NetworkFactory / NetworkAgent model does not deal well with the situation where a
+ * capability's presence cannot be known in advance. If such a capability is requested, then we
+ * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
+ * get immediately torn down because they do not have the requested capability.
+ */
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
+ private static final long NON_REQUESTABLE_CAPABILITIES =
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ /**
+ * Capabilities that are set by default when the object is constructed.
+ */
+ private static final long DEFAULT_CAPABILITIES =
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN);
+
+ /**
+ * Capabilities that suggest that a network is restricted.
+ * {@see #maybeMarkCapabilitiesRestricted}, {@see #FORCE_RESTRICTED_CAPABILITIES}
+ */
+ @VisibleForTesting
+ /* package */ static final long RESTRICTED_CAPABILITIES =
+ (1 << NET_CAPABILITY_CBS)
+ | (1 << NET_CAPABILITY_DUN)
+ | (1 << NET_CAPABILITY_EIMS)
+ | (1 << NET_CAPABILITY_FOTA)
+ | (1 << NET_CAPABILITY_IA)
+ | (1 << NET_CAPABILITY_IMS)
+ | (1 << NET_CAPABILITY_MCX)
+ | (1 << NET_CAPABILITY_RCS)
+ | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
+ | (1 << NET_CAPABILITY_XCAP);
+
+ /**
+ * Capabilities that force network to be restricted.
+ * {@see #maybeMarkCapabilitiesRestricted}.
+ */
+ private static final long FORCE_RESTRICTED_CAPABILITIES =
+ (1 << NET_CAPABILITY_OEM_PAID)
+ | (1 << NET_CAPABILITY_OEM_PRIVATE);
+
+ /**
+ * Capabilities that suggest that a network is unrestricted.
+ * {@see #maybeMarkCapabilitiesRestricted}.
+ */
+ @VisibleForTesting
+ /* package */ static final long UNRESTRICTED_CAPABILITIES =
+ (1 << NET_CAPABILITY_INTERNET)
+ | (1 << NET_CAPABILITY_MMS)
+ | (1 << NET_CAPABILITY_SUPL)
+ | (1 << NET_CAPABILITY_WIFI_P2P);
+
+ /**
+ * Capabilities that are managed by ConnectivityService.
+ */
+ private static final long CONNECTIVITY_MANAGED_CAPABILITIES =
+ (1 << NET_CAPABILITY_VALIDATED)
+ | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+ | (1 << NET_CAPABILITY_FOREGROUND)
+ | (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)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
+
+ /**
+ * Adds the given capability to this {@code NetworkCapability} instance.
+ * 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
+ */
+ 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
+ return this;
+ }
+
+ /**
+ * Adds the given capability to the list of unwanted capabilities of this
+ * {@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
+ * it will be removed from the list of required capabilities as well.
+ *
+ * @see #addCapability(int)
+ * @hide
+ */
+ public void addUnwantedCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+ mUnwantedNetworkCapabilities |= 1 << capability;
+ mNetworkCapabilities &= ~(1 << capability); // remove from requested capabilities
+ }
+
+ /**
+ * Removes (if found) the given capability from this {@code NetworkCapability} instance.
+ *
+ * @param capability the capability to be removed.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ 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;
+ mUnwantedNetworkCapabilities &= mask;
+ return this;
+ }
+
+ /**
+ * Sets (or clears) the given capability on this {@link NetworkCapabilities}
+ * instance.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setCapability(@NetCapability int capability,
+ boolean value) {
+ if (value) {
+ addCapability(capability);
+ } else {
+ removeCapability(capability);
+ }
+ return this;
+ }
+
+ /**
+ * Gets all the capabilities set on this {@code NetworkCapability} instance.
+ *
+ * @return an array of capability values for this instance.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public @NetCapability int[] getCapabilities() {
+ return BitUtils.unpackBits(mNetworkCapabilities);
+ }
+
+ /**
+ * Gets all the unwanted capabilities set on this {@code NetworkCapability} instance.
+ *
+ * @return an array of unwanted capability values for this instance.
+ * @hide
+ */
+ public @NetCapability int[] getUnwantedCapabilities() {
+ return BitUtils.unpackBits(mUnwantedNetworkCapabilities);
+ }
+
+
+ /**
+ * Sets all the capabilities set on this {@code NetworkCapability} instance.
+ * This overwrites any existing capabilities.
+ *
+ * @hide
+ */
+ public void setCapabilities(@NetCapability int[] capabilities,
+ @NetCapability int[] unwantedCapabilities) {
+ mNetworkCapabilities = BitUtils.packBits(capabilities);
+ mUnwantedNetworkCapabilities = BitUtils.packBits(unwantedCapabilities);
+ }
+
+ /**
+ * @deprecated use {@link #setCapabilities(int[], int[])}
+ * @hide
+ */
+ @Deprecated
+ public void setCapabilities(@NetCapability int[] capabilities) {
+ setCapabilities(capabilities, new int[] {});
+ }
+
+ /**
+ * Tests for the presence of a capability on this instance.
+ *
+ * @param capability the capabilities to be tested for.
+ * @return {@code true} if set on this instance.
+ */
+ public boolean hasCapability(@NetCapability int capability) {
+ return isValidCapability(capability)
+ && ((mNetworkCapabilities & (1 << capability)) != 0);
+ }
+
+ /** @hide */
+ public boolean hasUnwantedCapability(@NetCapability int capability) {
+ return isValidCapability(capability)
+ && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
+ }
+
+ /**
+ * Check if this NetworkCapabilities has system managed capabilities or not.
+ * @hide
+ */
+ public boolean hasConnectivityManagedCapability() {
+ return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
+ }
+
+ /** Note this method may result in having the same capability in wanted and unwanted lists. */
+ private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
+ this.mNetworkCapabilities |= nc.mNetworkCapabilities;
+ this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
+ }
+
+ /**
+ * Convenience function that returns a human-readable description of the first mutable
+ * capability we find. Used to present an error message to apps that request mutable
+ * capabilities.
+ *
+ * @hide
+ */
+ public @Nullable String describeFirstNonRequestableCapability() {
+ final long nonRequestable = (mNetworkCapabilities | mUnwantedNetworkCapabilities)
+ & NON_REQUESTABLE_CAPABILITIES;
+
+ if (nonRequestable != 0) {
+ return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
+ }
+ if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
+ if (hasSignalStrength()) return "signalStrength";
+ if (isPrivateDnsBroken()) {
+ return "privateDnsBroken";
+ }
+ return null;
+ }
+
+ private boolean satisfiedByNetCapabilities(@NonNull NetworkCapabilities nc,
+ boolean onlyImmutable) {
+ long requestedCapabilities = mNetworkCapabilities;
+ long requestedUnwantedCapabilities = mUnwantedNetworkCapabilities;
+ long providedCapabilities = nc.mNetworkCapabilities;
+
+ if (onlyImmutable) {
+ requestedCapabilities &= ~MUTABLE_CAPABILITIES;
+ requestedUnwantedCapabilities &= ~MUTABLE_CAPABILITIES;
+ }
+ return ((providedCapabilities & requestedCapabilities) == requestedCapabilities)
+ && ((requestedUnwantedCapabilities & providedCapabilities) == 0);
+ }
+
+ /** @hide */
+ public boolean equalsNetCapabilities(@NonNull NetworkCapabilities nc) {
+ return (nc.mNetworkCapabilities == this.mNetworkCapabilities)
+ && (nc.mUnwantedNetworkCapabilities == this.mUnwantedNetworkCapabilities);
+ }
+
+ private boolean equalsNetCapabilitiesRequestable(@NonNull NetworkCapabilities that) {
+ return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
+ (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES))
+ && ((this.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
+ (that.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
+ }
+
+ /**
+ * Deduces that all the capabilities it provides are typically provided by restricted networks
+ * or not.
+ *
+ * @return {@code true} if the network should be restricted.
+ * @hide
+ */
+ public boolean deduceRestrictedCapability() {
+ // Check if we have any capability that forces the network to be restricted.
+ final boolean forceRestrictedCapability =
+ (mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
+
+ // Verify there aren't any unrestricted capabilities. If there are we say
+ // the whole thing is unrestricted unless it is forced to be restricted.
+ final boolean hasUnrestrictedCapabilities =
+ (mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0;
+
+ // Must have at least some restricted capabilities.
+ final boolean hasRestrictedCapabilities =
+ (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
+
+ 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});
+ }
+ // There is no need to clear the UIDs, they have already been cleared by clearAll() above.
+ }
+
+ /**
+ * 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
+ * any of them. If a caller doesn't care about the transport it should not specify any.
+ */
+ private long mTransportTypes;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "TRANSPORT_" }, value = {
+ TRANSPORT_CELLULAR,
+ TRANSPORT_WIFI,
+ TRANSPORT_BLUETOOTH,
+ TRANSPORT_ETHERNET,
+ TRANSPORT_VPN,
+ TRANSPORT_WIFI_AWARE,
+ TRANSPORT_LOWPAN,
+ TRANSPORT_TEST,
+ })
+ public @interface Transport { }
+
+ /**
+ * Indicates this network uses a Cellular transport.
+ */
+ public static final int TRANSPORT_CELLULAR = 0;
+
+ /**
+ * Indicates this network uses a Wi-Fi transport.
+ */
+ public static final int TRANSPORT_WIFI = 1;
+
+ /**
+ * Indicates this network uses a Bluetooth transport.
+ */
+ public static final int TRANSPORT_BLUETOOTH = 2;
+
+ /**
+ * Indicates this network uses an Ethernet transport.
+ */
+ public static final int TRANSPORT_ETHERNET = 3;
+
+ /**
+ * Indicates this network uses a VPN transport.
+ */
+ public static final int TRANSPORT_VPN = 4;
+
+ /**
+ * Indicates this network uses a Wi-Fi Aware transport.
+ */
+ public static final int TRANSPORT_WIFI_AWARE = 5;
+
+ /**
+ * Indicates this network uses a LoWPAN transport.
+ */
+ public static final int TRANSPORT_LOWPAN = 6;
+
+ /**
+ * Indicates this network uses a Test-only virtual interface as a transport.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int TRANSPORT_TEST = 7;
+
+ /** @hide */
+ public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
+ /** @hide */
+ public static final int MAX_TRANSPORT = TRANSPORT_TEST;
+
+ /** @hide */
+ public static boolean isValidTransport(@Transport int transportType) {
+ return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
+ }
+
+ private static final String[] TRANSPORT_NAMES = {
+ "CELLULAR",
+ "WIFI",
+ "BLUETOOTH",
+ "ETHERNET",
+ "VPN",
+ "WIFI_AWARE",
+ "LOWPAN",
+ "TEST"
+ };
+
+ /**
+ * 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
+ // Test VPN networks can be created but their UID ranges must be empty.
+ | 1 << TRANSPORT_VPN;
+
+ /**
+ * Adds the given transport type to this {@code NetworkCapability} instance.
+ * 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
+ * to be selected. This is logically different than
+ * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
+ *
+ * @param transportType the transport type to be added.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities addTransportType(@Transport int transportType) {
+ checkValidTransportType(transportType);
+ mTransportTypes |= 1 << transportType;
+ setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given transport from this {@code NetworkCapability} instance.
+ *
+ * @param transportType the transport type to be removed.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities removeTransportType(@Transport int transportType) {
+ checkValidTransportType(transportType);
+ mTransportTypes &= ~(1 << transportType);
+ setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
+ return this;
+ }
+
+ /**
+ * Sets (or clears) the given transport on this {@link NetworkCapabilities}
+ * instance.
+ *
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setTransportType(@Transport int transportType,
+ boolean value) {
+ if (value) {
+ addTransportType(transportType);
+ } else {
+ removeTransportType(transportType);
+ }
+ return this;
+ }
+
+ /**
+ * Gets all the transports set on this {@code NetworkCapability} instance.
+ *
+ * @return an array of transport type values for this instance.
+ * @hide
+ */
+ @SystemApi
+ @NonNull public @Transport int[] getTransportTypes() {
+ return BitUtils.unpackBits(mTransportTypes);
+ }
+
+ /**
+ * Sets all the transports set on this {@code NetworkCapability} instance.
+ * This overwrites any existing transports.
+ *
+ * @hide
+ */
+ public void setTransportTypes(@Transport int[] transportTypes) {
+ mTransportTypes = BitUtils.packBits(transportTypes);
+ }
+
+ /**
+ * Tests for the presence of a transport on this instance.
+ *
+ * @param transportType the transport type to be tested for.
+ * @return {@code true} if set on this instance.
+ */
+ public boolean hasTransport(@Transport int transportType) {
+ return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
+ }
+
+ private void combineTransportTypes(NetworkCapabilities nc) {
+ this.mTransportTypes |= nc.mTransportTypes;
+ }
+
+ private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
+ return ((this.mTransportTypes == 0)
+ || ((this.mTransportTypes & nc.mTransportTypes) != 0));
+ }
+
+ /** @hide */
+ public boolean equalsTransportTypes(NetworkCapabilities nc) {
+ return (nc.mTransportTypes == this.mTransportTypes);
+ }
+
+ /**
+ * UID of the app that owns this network, or Process#INVALID_UID if none/unknown.
+ *
+ * <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.
+ *
+ * <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 mOwnerUid = Process.INVALID_UID;
+
+ /**
+ * Set the UID of the owner app.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
+ mOwnerUid = uid;
+ return this;
+ }
+
+ /**
+ * 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;
+ }
+
+ private boolean equalsOwnerUid(@NonNull final NetworkCapabilities nc) {
+ return mOwnerUid == nc.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
+ 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");
+ }
+ }
+
+ /**
+ * Value indicating that link bandwidth is unspecified.
+ * @hide
+ */
+ public static final int LINK_BANDWIDTH_UNSPECIFIED = 0;
+
+ /**
+ * Passive link bandwidth. This is a rough guide of the expected peak bandwidth
+ * for the first hop on the given transport. It is not measured, but may take into account
+ * link parameters (Radio technology, allocated channels, etc).
+ */
+ private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+ private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+
+ /**
+ * Sets the upstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * {@see Builder#setLinkUpstreamBandwidthKbps}
+ *
+ * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) {
+ mLinkUpBandwidthKbps = upKbps;
+ return this;
+ }
+
+ /**
+ * Retrieves the upstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ *
+ * @return The estimated first hop upstream (device to network) bandwidth.
+ */
+ public int getLinkUpstreamBandwidthKbps() {
+ return mLinkUpBandwidthKbps;
+ }
+
+ /**
+ * Sets the downstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ * <p>
+ * {@see Builder#setLinkUpstreamBandwidthKbps}
+ *
+ * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) {
+ mLinkDownBandwidthKbps = downKbps;
+ return this;
+ }
+
+ /**
+ * Retrieves the downstream bandwidth for this network in Kbps. This always only refers to
+ * the estimated first hop transport bandwidth.
+ *
+ * @return The estimated first hop downstream (network to device) bandwidth.
+ */
+ public int getLinkDownstreamBandwidthKbps() {
+ return mLinkDownBandwidthKbps;
+ }
+
+ private void combineLinkBandwidths(NetworkCapabilities nc) {
+ this.mLinkUpBandwidthKbps =
+ Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
+ this.mLinkDownBandwidthKbps =
+ Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
+ }
+ private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
+ return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps
+ || this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
+ }
+ private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
+ return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps
+ && this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
+ }
+ /** @hide */
+ public static int minBandwidth(int a, int b) {
+ if (a == LINK_BANDWIDTH_UNSPECIFIED) {
+ return b;
+ } else if (b == LINK_BANDWIDTH_UNSPECIFIED) {
+ return a;
+ } else {
+ return Math.min(a, b);
+ }
+ }
+ /** @hide */
+ public static int maxBandwidth(int a, int b) {
+ return Math.max(a, b);
+ }
+
+ private NetworkSpecifier mNetworkSpecifier = null;
+ private TransportInfo mTransportInfo = null;
+
+ /**
+ * 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 networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ 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");
+ }
+
+ mNetworkSpecifier = networkSpecifier;
+
+ return this;
+ }
+
+ /**
+ * Sets the optional transport specific information.
+ *
+ * @param transportInfo A concrete, parcelable framework class that extends
+ * {@link TransportInfo}.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setTransportInfo(@NonNull TransportInfo transportInfo) {
+ mTransportInfo = transportInfo;
+ return this;
+ }
+
+ /**
+ * 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}.
+ */
+ public @Nullable NetworkSpecifier getNetworkSpecifier() {
+ return mNetworkSpecifier;
+ }
+
+ /**
+ * Returns a transport-specific information container. The application may cast this
+ * container to a concrete sub-class based on its knowledge of the network request. The
+ * application should be able to deal with a {@code null} return value or an invalid case,
+ * e.g. use {@code instanceof} operator to verify expected type.
+ *
+ * @return A concrete implementation of the {@link TransportInfo} class or null if not
+ * available for the network.
+ */
+ @Nullable public TransportInfo getTransportInfo() {
+ return mTransportInfo;
+ }
+
+ private void combineSpecifiers(NetworkCapabilities nc) {
+ if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
+ throw new IllegalStateException("Can't combine two networkSpecifiers");
+ }
+ setNetworkSpecifier(nc.mNetworkSpecifier);
+ }
+
+ private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
+ return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
+ || nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
+ }
+
+ private boolean equalsSpecifier(NetworkCapabilities nc) {
+ return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
+ }
+
+ private void combineTransportInfos(NetworkCapabilities nc) {
+ if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
+ throw new IllegalStateException("Can't combine two TransportInfos");
+ }
+ setTransportInfo(nc.mTransportInfo);
+ }
+
+ private boolean equalsTransportInfo(NetworkCapabilities nc) {
+ return Objects.equals(mTransportInfo, nc.mTransportInfo);
+ }
+
+ /**
+ * Magic value that indicates no signal strength provided. A request specifying this value is
+ * always satisfied.
+ */
+ public static final int SIGNAL_STRENGTH_UNSPECIFIED = Integer.MIN_VALUE;
+
+ /**
+ * Signal strength. This is a signed integer, and higher values indicate better signal.
+ * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ private int mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
+
+ /**
+ * 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.
+ *
+ * @param signalStrength the bearer-specific signal strength.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setSignalStrength(int signalStrength) {
+ mSignalStrength = signalStrength;
+ return this;
+ }
+
+ /**
+ * Returns {@code true} if this object specifies a signal strength.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public boolean hasSignalStrength() {
+ return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED;
+ }
+
+ /**
+ * Retrieves the signal strength.
+ *
+ * @return The bearer-specific signal strength.
+ */
+ public int getSignalStrength() {
+ return mSignalStrength;
+ }
+
+ private void combineSignalStrength(NetworkCapabilities nc) {
+ this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
+ }
+
+ private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
+ return this.mSignalStrength <= nc.mSignalStrength;
+ }
+
+ private boolean equalsSignalStrength(NetworkCapabilities nc) {
+ return this.mSignalStrength == nc.mSignalStrength;
+ }
+
+ /**
+ * List of UIDs this network applies to. No restriction if null.
+ * <p>
+ * For networks, mUids represent the list of network this applies to, and null means this
+ * network applies to all UIDs.
+ * For requests, mUids is the list of UIDs this network MUST apply to to match ; ALL UIDs
+ * must be included in a network so that they match. As an exception to the general rule,
+ * a null mUids field for requests mean "no requirements" rather than what the general rule
+ * would suggest ("must apply to all UIDs") : this is because this has shown to be what users
+ * of this API expect in practice. A network that must match all UIDs can still be
+ * expressed with a set ranging the entire set of possible UIDs.
+ * <p>
+ * mUids is typically (and at this time, only) used by VPN. This network is only available to
+ * the UIDs in this list, and it is their default network. Apps in this list that wish to
+ * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this
+ * 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
+ * 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>
+ * Please note that in principle a single app can be associated with multiple UIDs because
+ * each app will have a different UID when it's run as a different (macro-)user. A single
+ * macro user can only have a single active VPN app at any given time however.
+ * <p>
+ * Also please be aware this class does not try to enforce any normalization on this. Callers
+ * can only alter the UIDs by setting them wholesale : this class does not provide any utility
+ * to add or remove individual UIDs or ranges. If callers have any normalization needs on
+ * their own (like requiring sortedness or no overlap) they need to enforce it
+ * themselves. Some of the internal methods also assume this is normalized as in no adjacent
+ * or overlapping ranges are present.
+ *
+ * @hide
+ */
+ private ArraySet<UidRange> mUids = null;
+
+ /**
+ * Convenience method to set the UIDs this network applies to to a single UID.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setSingleUid(int uid) {
+ final ArraySet<UidRange> identity = new ArraySet<>(1);
+ identity.add(new UidRange(uid, uid));
+ setUids(identity);
+ return this;
+ }
+
+ /**
+ * Set the list of UIDs this network applies to.
+ * This makes a copy of the set so that callers can't modify it after the call.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setUids(Set<UidRange> uids) {
+ if (null == uids) {
+ mUids = null;
+ } else {
+ mUids = new ArraySet<>(uids);
+ }
+ return this;
+ }
+
+ /**
+ * Get the list of UIDs this network applies to.
+ * This returns a copy of the set so that callers can't modify the original object.
+ * @hide
+ */
+ public @Nullable Set<UidRange> getUids() {
+ return null == mUids ? null : new ArraySet<>(mUids);
+ }
+
+ /**
+ * Test whether this network applies to this UID.
+ * @hide
+ */
+ public boolean appliesToUid(int uid) {
+ if (null == mUids) return true;
+ for (UidRange range : mUids) {
+ if (range.contains(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests if the set of UIDs that this network applies to is the same as the passed network.
+ * <p>
+ * This test only checks whether equal range objects are in both sets. It will
+ * return false if the ranges are not exactly the same, even if the covered UIDs
+ * are for an equivalent result.
+ * <p>
+ * Note that this method is not very optimized, which is fine as long as it's not used very
+ * often.
+ * <p>
+ * nc is assumed nonnull.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean equalsUids(@NonNull NetworkCapabilities nc) {
+ Set<UidRange> comparedUids = nc.mUids;
+ if (null == comparedUids) return null == mUids;
+ if (null == mUids) return false;
+ // Make a copy so it can be mutated to check that all ranges in mUids
+ // also are in uids.
+ final Set<UidRange> uids = new ArraySet<>(mUids);
+ for (UidRange range : comparedUids) {
+ if (!uids.contains(range)) {
+ return false;
+ }
+ uids.remove(range);
+ }
+ return uids.isEmpty();
+ }
+
+ /**
+ * Test whether the passed NetworkCapabilities satisfies the UIDs this capabilities require.
+ *
+ * This method is called on the NetworkCapabilities embedded in a request with the
+ * capabilities of an available network. It checks whether all the UIDs from this listen
+ * (representing the UIDs that must have access to the network) are satisfied by the UIDs
+ * 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
+ * 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>
+ * nc is assumed nonnull. Else, NPE.
+ * @see #appliesToUid
+ * @hide
+ */
+ 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.mOwnerUid)) return true;
+ if (!nc.appliesToUidRange(requiredRange)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether this network applies to the passed ranges.
+ * This assumes that to apply, the passed range has to be entirely contained
+ * within one of the ranges this network applies to. If the ranges are not normalized,
+ * this method may return false even though all required UIDs are covered because no
+ * single range contained them all.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean appliesToUidRange(@Nullable UidRange requiredRange) {
+ if (null == mUids) return true;
+ for (UidRange uidRange : mUids) {
+ if (uidRange.containsRange(requiredRange)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Combine the UIDs this network currently applies to with the UIDs the passed
+ * NetworkCapabilities apply to.
+ * nc is assumed nonnull.
+ */
+ private void combineUids(@NonNull NetworkCapabilities nc) {
+ if (null == nc.mUids || null == mUids) {
+ mUids = null;
+ return;
+ }
+ mUids.addAll(nc.mUids);
+ }
+
+
+ /**
+ * The SSID of the network, or null if not applicable or unknown.
+ * <p>
+ * This is filled in by wifi code.
+ * @hide
+ */
+ private String mSSID;
+
+ /**
+ * Sets the SSID of this network.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setSSID(@Nullable String ssid) {
+ mSSID = ssid;
+ return this;
+ }
+
+ /**
+ * Gets the SSID of this network, or null if none or unknown.
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getSsid() {
+ return mSSID;
+ }
+
+ /**
+ * Tests if the SSID of this network is the same as the SSID of the passed network.
+ * @hide
+ */
+ public boolean equalsSSID(@NonNull NetworkCapabilities nc) {
+ return Objects.equals(mSSID, nc.mSSID);
+ }
+
+ /**
+ * Check if the SSID requirements of this object are matched by the passed object.
+ * @hide
+ */
+ public boolean satisfiedBySSID(@NonNull NetworkCapabilities nc) {
+ return mSSID == null || mSSID.equals(nc.mSSID);
+ }
+
+ /**
+ * Combine SSIDs of the capabilities.
+ * <p>
+ * This is only legal if either the SSID of this object is null, or both SSIDs are
+ * equal.
+ * @hide
+ */
+ private void combineSSIDs(@NonNull NetworkCapabilities nc) {
+ if (mSSID != null && !mSSID.equals(nc.mSSID)) {
+ throw new IllegalStateException("Can't combine two SSIDs");
+ }
+ setSSID(nc.mSSID);
+ }
+
+ /**
+ * Combine a set of Capabilities to this one. Useful for coming up with the complete set.
+ * <p>
+ * Note that this method may break an invariant of having a particular capability in either
+ * wanted or unwanted lists but never in both. Requests that have the same capability in
+ * both lists will never be satisfied.
+ * @hide
+ */
+ public void combineCapabilities(@NonNull NetworkCapabilities nc) {
+ combineNetCapabilities(nc);
+ combineTransportTypes(nc);
+ combineLinkBandwidths(nc);
+ combineSpecifiers(nc);
+ combineTransportInfos(nc);
+ combineSignalStrength(nc);
+ combineUids(nc);
+ combineSSIDs(nc);
+ combineRequestor(nc);
+ combineAdministratorUids(nc);
+ }
+
+ /**
+ * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ * @param onlyImmutable if {@code true}, do not consider mutable requirements such as link
+ * bandwidth, signal strength, or validation / captive portal status.
+ *
+ * @hide
+ */
+ private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
+ return (nc != null
+ && satisfiedByNetCapabilities(nc, onlyImmutable)
+ && satisfiedByTransportTypes(nc)
+ && (onlyImmutable || satisfiedByLinkBandwidths(nc))
+ && satisfiedBySpecifier(nc)
+ && (onlyImmutable || satisfiedBySignalStrength(nc))
+ && (onlyImmutable || satisfiedByUids(nc))
+ && (onlyImmutable || satisfiedBySSID(nc)))
+ && (onlyImmutable || satisfiedByRequestor(nc));
+ }
+
+ /**
+ * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean satisfiedByNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+ return satisfiedByNetworkCapabilities(nc, false);
+ }
+
+ /**
+ * Check if our immutable requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ *
+ * @hide
+ */
+ public boolean satisfiedByImmutableNetworkCapabilities(@Nullable NetworkCapabilities nc) {
+ return satisfiedByNetworkCapabilities(nc, true);
+ }
+
+ /**
+ * Checks that our immutable capabilities are the same as those of the given
+ * {@code NetworkCapabilities} and return a String describing any difference.
+ * The returned String is empty if there is no difference.
+ *
+ * @hide
+ */
+ public String describeImmutableDifferences(@Nullable NetworkCapabilities that) {
+ if (that == null) {
+ return "other NetworkCapabilities was null";
+ }
+
+ StringJoiner joiner = new StringJoiner(", ");
+
+ // Ignore NOT_METERED being added or removed as it is effectively dynamic. http://b/63326103
+ // TODO: properly support NOT_METERED as a mutable and requestable capability.
+ final long mask = ~MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_NOT_METERED);
+ long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
+ long newImmutableCapabilities = that.mNetworkCapabilities & mask;
+ if (oldImmutableCapabilities != newImmutableCapabilities) {
+ String before = capabilityNamesOf(BitUtils.unpackBits(oldImmutableCapabilities));
+ String after = capabilityNamesOf(BitUtils.unpackBits(newImmutableCapabilities));
+ joiner.add(String.format("immutable capabilities changed: %s -> %s", before, after));
+ }
+
+ if (!equalsSpecifier(that)) {
+ NetworkSpecifier before = this.getNetworkSpecifier();
+ NetworkSpecifier after = that.getNetworkSpecifier();
+ joiner.add(String.format("specifier changed: %s -> %s", before, after));
+ }
+
+ if (!equalsTransportTypes(that)) {
+ String before = transportNamesOf(this.getTransportTypes());
+ String after = transportNamesOf(that.getTransportTypes());
+ joiner.add(String.format("transports changed: %s -> %s", before, after));
+ }
+
+ return joiner.toString();
+ }
+
+ /**
+ * Checks that our requestable capabilities are the same as those of the given
+ * {@code NetworkCapabilities}.
+ *
+ * @hide
+ */
+ public boolean equalRequestableCapabilities(@Nullable NetworkCapabilities nc) {
+ if (nc == null) return false;
+ return (equalsNetCapabilitiesRequestable(nc)
+ && equalsTransportTypes(nc)
+ && equalsSpecifier(nc));
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
+ NetworkCapabilities that = (NetworkCapabilities) obj;
+ return equalsNetCapabilities(that)
+ && equalsTransportTypes(that)
+ && equalsLinkBandwidths(that)
+ && equalsSignalStrength(that)
+ && equalsSpecifier(that)
+ && equalsTransportInfo(that)
+ && equalsUids(that)
+ && equalsSSID(that)
+ && equalsOwnerUid(that)
+ && equalsPrivateDnsBroken(that)
+ && equalsRequestor(that)
+ && equalsAdministratorUids(that);
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (mNetworkCapabilities & 0xFFFFFFFF)
+ + ((int) (mNetworkCapabilities >> 32) * 3)
+ + ((int) (mUnwantedNetworkCapabilities & 0xFFFFFFFF) * 5)
+ + ((int) (mUnwantedNetworkCapabilities >> 32) * 7)
+ + ((int) (mTransportTypes & 0xFFFFFFFF) * 11)
+ + ((int) (mTransportTypes >> 32) * 13)
+ + mLinkUpBandwidthKbps * 17
+ + mLinkDownBandwidthKbps * 19
+ + Objects.hashCode(mNetworkSpecifier) * 23
+ + mSignalStrength * 29
+ + mOwnerUid * 31
+ + Objects.hashCode(mUids) * 37
+ + Objects.hashCode(mSSID) * 41
+ + Objects.hashCode(mTransportInfo) * 43
+ + Objects.hashCode(mPrivateDnsBroken) * 47
+ + Objects.hashCode(mRequestorUid) * 53
+ + Objects.hashCode(mRequestorPackageName) * 59
+ + Arrays.hashCode(mAdministratorUids) * 61;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mNetworkCapabilities);
+ dest.writeLong(mUnwantedNetworkCapabilities);
+ dest.writeLong(mTransportTypes);
+ dest.writeInt(mLinkUpBandwidthKbps);
+ dest.writeInt(mLinkDownBandwidthKbps);
+ dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
+ dest.writeParcelable((Parcelable) mTransportInfo, flags);
+ 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 =
+ new Creator<NetworkCapabilities>() {
+ @Override
+ public NetworkCapabilities createFromParcel(Parcel in) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+
+ netCap.mNetworkCapabilities = in.readLong();
+ netCap.mUnwantedNetworkCapabilities = in.readLong();
+ netCap.mTransportTypes = in.readLong();
+ netCap.mLinkUpBandwidthKbps = in.readInt();
+ netCap.mLinkDownBandwidthKbps = in.readInt();
+ netCap.mNetworkSpecifier = in.readParcelable(null);
+ netCap.mTransportInfo = in.readParcelable(null);
+ netCap.mSignalStrength = in.readInt();
+ 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
+ public NetworkCapabilities[] newArray(int size) {
+ return new NetworkCapabilities[size];
+ }
+ };
+
+ @Override
+ public @NonNull String toString() {
+ final StringBuilder sb = new StringBuilder("[");
+ if (0 != mTransportTypes) {
+ sb.append(" Transports: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mTransportTypes,
+ NetworkCapabilities::transportNameOf, "|");
+ }
+ if (0 != mNetworkCapabilities) {
+ sb.append(" Capabilities: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
+ NetworkCapabilities::capabilityNameOf, "&");
+ }
+ if (0 != mUnwantedNetworkCapabilities) {
+ sb.append(" Unwanted: ");
+ appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
+ NetworkCapabilities::capabilityNameOf, "&");
+ }
+ if (mLinkUpBandwidthKbps > 0) {
+ sb.append(" LinkUpBandwidth>=").append(mLinkUpBandwidthKbps).append("Kbps");
+ }
+ if (mLinkDownBandwidthKbps > 0) {
+ sb.append(" LinkDnBandwidth>=").append(mLinkDownBandwidthKbps).append("Kbps");
+ }
+ if (mNetworkSpecifier != null) {
+ sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
+ }
+ if (mTransportInfo != null) {
+ sb.append(" TransportInfo: <").append(mTransportInfo).append(">");
+ }
+ if (hasSignalStrength()) {
+ sb.append(" SignalStrength: ").append(mSignalStrength);
+ }
+
+ if (null != mUids) {
+ if ((1 == mUids.size()) && (mUids.valueAt(0).count() == 1)) {
+ sb.append(" Uid: ").append(mUids.valueAt(0).start);
+ } else {
+ sb.append(" Uids: <").append(mUids).append(">");
+ }
+ }
+ if (mOwnerUid != Process.INVALID_UID) {
+ sb.append(" OwnerUid: ").append(mOwnerUid);
+ }
+
+ if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
+ }
+
+ if (mRequestorUid != Process.INVALID_UID) {
+ sb.append(" RequestorUid: ").append(mRequestorUid);
+ }
+
+ if (mRequestorPackageName != null) {
+ sb.append(" RequestorPkg: ").append(mRequestorPackageName);
+ }
+
+ if (null != mSSID) {
+ sb.append(" SSID: ").append(mSSID);
+ }
+
+
+ if (mPrivateDnsBroken) {
+ sb.append(" PrivateDnsBroken");
+ }
+
+ sb.append("]");
+ return sb.toString();
+ }
+
+
+ private interface NameOf {
+ String nameOf(int value);
+ }
+
+ /**
+ * @hide
+ */
+ public static void appendStringRepresentationOfBitMaskToStringBuilder(@NonNull StringBuilder sb,
+ long bitMask, @NonNull NameOf nameFetcher, @NonNull String separator) {
+ int bitPos = 0;
+ boolean firstElementAdded = false;
+ while (bitMask != 0) {
+ if ((bitMask & 1) != 0) {
+ if (firstElementAdded) {
+ sb.append(separator);
+ } else {
+ firstElementAdded = true;
+ }
+ sb.append(nameFetcher.nameOf(bitPos));
+ }
+ bitMask >>= 1;
+ ++bitPos;
+ }
+ }
+
+ /** @hide */
+ public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ for (int transport : getTransportTypes()) {
+ proto.write(NetworkCapabilitiesProto.TRANSPORTS, transport);
+ }
+
+ for (int capability : getCapabilities()) {
+ proto.write(NetworkCapabilitiesProto.CAPABILITIES, capability);
+ }
+
+ proto.write(NetworkCapabilitiesProto.LINK_UP_BANDWIDTH_KBPS, mLinkUpBandwidthKbps);
+ proto.write(NetworkCapabilitiesProto.LINK_DOWN_BANDWIDTH_KBPS, mLinkDownBandwidthKbps);
+
+ if (mNetworkSpecifier != null) {
+ proto.write(NetworkCapabilitiesProto.NETWORK_SPECIFIER, mNetworkSpecifier.toString());
+ }
+ if (mTransportInfo != null) {
+ // TODO b/120653863: write transport-specific info to proto?
+ }
+
+ proto.write(NetworkCapabilitiesProto.CAN_REPORT_SIGNAL_STRENGTH, hasSignalStrength());
+ proto.write(NetworkCapabilitiesProto.SIGNAL_STRENGTH, mSignalStrength);
+
+ proto.end(token);
+ }
+
+ /**
+ * @hide
+ */
+ public static @NonNull String capabilityNamesOf(@Nullable @NetCapability int[] capabilities) {
+ StringJoiner joiner = new StringJoiner("|");
+ if (capabilities != null) {
+ for (int c : capabilities) {
+ joiner.add(capabilityNameOf(c));
+ }
+ }
+ return joiner.toString();
+ }
+
+ /**
+ * @hide
+ */
+ public static @NonNull String capabilityNameOf(@NetCapability int capability) {
+ switch (capability) {
+ case NET_CAPABILITY_MMS: return "MMS";
+ case NET_CAPABILITY_SUPL: return "SUPL";
+ case NET_CAPABILITY_DUN: return "DUN";
+ case NET_CAPABILITY_FOTA: return "FOTA";
+ case NET_CAPABILITY_IMS: return "IMS";
+ case NET_CAPABILITY_CBS: return "CBS";
+ case NET_CAPABILITY_WIFI_P2P: return "WIFI_P2P";
+ case NET_CAPABILITY_IA: return "IA";
+ case NET_CAPABILITY_RCS: return "RCS";
+ case NET_CAPABILITY_XCAP: return "XCAP";
+ case NET_CAPABILITY_EIMS: return "EIMS";
+ case NET_CAPABILITY_NOT_METERED: return "NOT_METERED";
+ case NET_CAPABILITY_INTERNET: return "INTERNET";
+ case NET_CAPABILITY_NOT_RESTRICTED: return "NOT_RESTRICTED";
+ case NET_CAPABILITY_TRUSTED: return "TRUSTED";
+ case NET_CAPABILITY_NOT_VPN: return "NOT_VPN";
+ case NET_CAPABILITY_VALIDATED: return "VALIDATED";
+ case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
+ case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
+ case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
+ case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
+ case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
+ 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";
+ case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
+ case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
+ default: return Integer.toString(capability);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static @NonNull String transportNamesOf(@Nullable @Transport int[] types) {
+ StringJoiner joiner = new StringJoiner("|");
+ if (types != null) {
+ for (int t : types) {
+ joiner.add(transportNameOf(t));
+ }
+ }
+ return joiner.toString();
+ }
+
+ /**
+ * @hide
+ */
+ public static @NonNull String transportNameOf(@Transport int transport) {
+ if (!isValidTransport(transport)) {
+ return "UNKNOWN";
+ }
+ return TRANSPORT_NAMES[transport];
+ }
+
+ private static void checkValidTransportType(@Transport int transport) {
+ Preconditions.checkArgument(
+ isValidTransport(transport), "Invalid TransportType " + transport);
+ }
+
+ private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) {
+ return capability >= MIN_NET_CAPABILITY && capability <= MAX_NET_CAPABILITY;
+ }
+
+ private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) {
+ Preconditions.checkArgument(isValidCapability(capability),
+ "NetworkCapability " + capability + "out of range");
+ }
+
+ /**
+ * Check if this {@code NetworkCapability} instance is metered.
+ *
+ * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+ * @hide
+ */
+ 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
+ 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/framework/src/android/net/NetworkConfig.java b/framework/src/android/net/NetworkConfig.java
new file mode 100644
index 0000000..32a2cda
--- /dev/null
+++ b/framework/src/android/net/NetworkConfig.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 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 java.util.Locale;
+
+/**
+ * Describes the buildtime configuration of a network.
+ * Holds settings read from resources.
+ * @hide
+ */
+public class NetworkConfig {
+ /**
+ * Human readable string
+ */
+ public String name;
+
+ /**
+ * Type from ConnectivityManager
+ */
+ public int type;
+
+ /**
+ * the radio number from radio attributes config
+ */
+ public int radio;
+
+ /**
+ * higher number == higher priority when turning off connections
+ */
+ public int priority;
+
+ /**
+ * indicates the boot time dependencyMet setting
+ */
+ public boolean dependencyMet;
+
+ /**
+ * indicates the default restoral timer in seconds
+ * if the network is used as a special network feature
+ * -1 indicates no restoration of default
+ */
+ public int restoreTime;
+
+ /**
+ * input string from config.xml resource. Uses the form:
+ * [Connection name],[ConnectivityManager connection type],
+ * [associated radio-type],[priority],[dependencyMet]
+ */
+ public NetworkConfig(String init) {
+ String fragments[] = init.split(",");
+ name = fragments[0].trim().toLowerCase(Locale.ROOT);
+ type = Integer.parseInt(fragments[1]);
+ radio = Integer.parseInt(fragments[2]);
+ priority = Integer.parseInt(fragments[3]);
+ restoreTime = Integer.parseInt(fragments[4]);
+ dependencyMet = Boolean.parseBoolean(fragments[5]);
+ }
+
+ /**
+ * Indicates if this network is supposed to be default-routable
+ */
+ public boolean isDefault() {
+ return (type == radio);
+ }
+}
diff --git a/framework/src/android/net/NetworkInfo.aidl b/framework/src/android/net/NetworkInfo.aidl
new file mode 100644
index 0000000..f501873
--- /dev/null
+++ b/framework/src/android/net/NetworkInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2007, 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;
+
+parcelable NetworkInfo;
diff --git a/framework/src/android/net/NetworkInfo.java b/framework/src/android/net/NetworkInfo.java
new file mode 100644
index 0000000..d752901
--- /dev/null
+++ b/framework/src/android/net/NetworkInfo.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2008 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.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.EnumMap;
+
+/**
+ * Describes the status of a network interface.
+ * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
+ * the current network connection.
+ *
+ * @deprecated Callers should instead use the {@link ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes, or switch to use
+ * {@link ConnectivityManager#getNetworkCapabilities} or
+ * {@link ConnectivityManager#getLinkProperties} to get information synchronously. Keep
+ * in mind that while callbacks are guaranteed to be called for every event in order,
+ * synchronous calls have no such constraints, and as such it is unadvisable to use the
+ * synchronous methods inside the callbacks as they will often not offer a view of
+ * networking that is consistent (that is: they may return a past or a future state with
+ * respect to the event being processed by the callback). Instead, callers are advised
+ * to only use the arguments of the callbacks, possibly memorizing the specific bits of
+ * information they need to keep from one callback to another.
+ */
+@Deprecated
+public class NetworkInfo implements Parcelable {
+
+ /**
+ * Coarse-grained network state. This is probably what most applications should
+ * use, rather than {@link android.net.NetworkInfo.DetailedState DetailedState}.
+ * The mapping between the two is as follows:
+ * <br/><br/>
+ * <table>
+ * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr>
+ * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr>
+ * <tr><td><code>SCANNING</code></td><td><code>DISCONNECTED</code></td></tr>
+ * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
+ * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
+ * <tr><td><code>OBTAINING_IPADDR</code></td><td><code>CONNECTING</code></td></tr>
+ * <tr><td><code>VERIFYING_POOR_LINK</code></td><td><code>CONNECTING</code></td></tr>
+ * <tr><td><code>CAPTIVE_PORTAL_CHECK</code></td><td><code>CONNECTING</code></td></tr>
+ * <tr><td><code>CONNECTED</code></td><td><code>CONNECTED</code></td></tr>
+ * <tr><td><code>SUSPENDED</code></td><td><code>SUSPENDED</code></td></tr>
+ * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
+ * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
+ * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
+ * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
+ * </table>
+ *
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ public enum State {
+ CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
+ }
+
+ /**
+ * The fine-grained state of a network connection. This level of detail
+ * is probably of interest to few applications. Most should use
+ * {@link android.net.NetworkInfo.State State} instead.
+ *
+ * @deprecated See {@link NetworkInfo}.
+ */
+ @Deprecated
+ public enum DetailedState {
+ /** Ready to start data connection setup. */
+ IDLE,
+ /** Searching for an available access point. */
+ SCANNING,
+ /** Currently setting up data connection. */
+ CONNECTING,
+ /** Network link established, performing authentication. */
+ AUTHENTICATING,
+ /** Awaiting response from DHCP server in order to assign IP address information. */
+ OBTAINING_IPADDR,
+ /** IP traffic should be available. */
+ CONNECTED,
+ /** IP traffic is suspended */
+ SUSPENDED,
+ /** Currently tearing down data connection. */
+ DISCONNECTING,
+ /** IP traffic not available. */
+ DISCONNECTED,
+ /** Attempt to connect failed. */
+ FAILED,
+ /** Access to this network is blocked. */
+ BLOCKED,
+ /** Link has poor connectivity. */
+ VERIFYING_POOR_LINK,
+ /** Checking if network is a captive portal */
+ CAPTIVE_PORTAL_CHECK
+ }
+
+ /**
+ * This is the map described in the Javadoc comment above. The positions
+ * of the elements of the array must correspond to the ordinal values
+ * of <code>DetailedState</code>.
+ */
+ private static final EnumMap<DetailedState, State> stateMap =
+ new EnumMap<DetailedState, State>(DetailedState.class);
+
+ static {
+ stateMap.put(DetailedState.IDLE, State.DISCONNECTED);
+ stateMap.put(DetailedState.SCANNING, State.DISCONNECTED);
+ stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
+ stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
+ stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
+ stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
+ stateMap.put(DetailedState.CAPTIVE_PORTAL_CHECK, State.CONNECTING);
+ stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
+ stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
+ stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
+ stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
+ stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
+ stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
+ }
+
+ private int mNetworkType;
+ private int mSubtype;
+ private String mTypeName;
+ private String mSubtypeName;
+ @NonNull
+ private State mState;
+ @NonNull
+ private DetailedState mDetailedState;
+ private String mReason;
+ private String mExtraInfo;
+ private boolean mIsFailover;
+ private boolean mIsAvailable;
+ private boolean mIsRoaming;
+
+ /**
+ * 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.
+ */
+ 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);
+ }
+ mNetworkType = type;
+ mSubtype = subtype;
+ mTypeName = typeName;
+ mSubtypeName = subtypeName;
+ setDetailedState(DetailedState.IDLE, null, null);
+ mState = State.UNKNOWN;
+ }
+
+ /** {@hide} */
+ @UnsupportedAppUsage
+ public NetworkInfo(NetworkInfo source) {
+ if (source != null) {
+ synchronized (source) {
+ mNetworkType = source.mNetworkType;
+ mSubtype = source.mSubtype;
+ mTypeName = source.mTypeName;
+ mSubtypeName = source.mSubtypeName;
+ mState = source.mState;
+ mDetailedState = source.mDetailedState;
+ mReason = source.mReason;
+ mExtraInfo = source.mExtraInfo;
+ mIsFailover = source.mIsFailover;
+ mIsAvailable = source.mIsAvailable;
+ mIsRoaming = source.mIsRoaming;
+ }
+ }
+ }
+
+ /**
+ * Reports the type of network to which the
+ * info in this {@code NetworkInfo} pertains.
+ * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
+ * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
+ * ConnectivityManager#TYPE_ETHERNET}, {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
+ * types defined by {@link ConnectivityManager}.
+ * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+ * instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+ * {@link #getType} and {@link #getTypeName} cannot account for networks using
+ * multiple transports. Note that generally apps should not care about transport;
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+ * {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+ * apps concerned with meteredness or bandwidth should be looking at, as they
+ * offer this information with much better accuracy.
+ */
+ @Deprecated
+ public int getType() {
+ synchronized (this) {
+ return mNetworkType;
+ }
+ }
+
+ /**
+ * @deprecated Use {@link NetworkCapabilities} instead
+ * @hide
+ */
+ @Deprecated
+ public void setType(int type) {
+ synchronized (this) {
+ mNetworkType = type;
+ }
+ }
+
+ /**
+ * Return a network-type-specific integer describing the subtype
+ * of the network.
+ * @return the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
+ */
+ @Deprecated
+ public int getSubtype() {
+ synchronized (this) {
+ return mSubtype;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public void setSubtype(int subtype, String subtypeName) {
+ synchronized (this) {
+ mSubtype = subtype;
+ mSubtypeName = subtypeName;
+ }
+ }
+
+ /**
+ * Return a human-readable name describe the type of the network,
+ * for example "WIFI" or "MOBILE".
+ * @return the name of the network type
+ * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+ * instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+ * {@link #getType} and {@link #getTypeName} cannot account for networks using
+ * multiple transports. Note that generally apps should not care about transport;
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+ * {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+ * apps concerned with meteredness or bandwidth should be looking at, as they
+ * offer this information with much better accuracy.
+ */
+ @Deprecated
+ public String getTypeName() {
+ synchronized (this) {
+ return mTypeName;
+ }
+ }
+
+ /**
+ * Return a human-readable name describing the subtype of the network.
+ * @return the name of the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
+ */
+ @Deprecated
+ public String getSubtypeName() {
+ synchronized (this) {
+ return mSubtypeName;
+ }
+ }
+
+ /**
+ * Indicates whether network connectivity exists or is in the process
+ * of being established. This is good for applications that need to
+ * do anything related to the network other than read or write data.
+ * For the latter, call {@link #isConnected()} instead, which guarantees
+ * that the network is fully usable.
+ * @return {@code true} if network connectivity exists or is in the process
+ * of being established, {@code false} otherwise.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes.
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ */
+ @Deprecated
+ public boolean isConnectedOrConnecting() {
+ synchronized (this) {
+ return mState == State.CONNECTED || mState == State.CONNECTING;
+ }
+ }
+
+ /**
+ * Indicates whether network connectivity exists and it is possible to establish
+ * connections and pass data.
+ * <p>Always call this before attempting to perform data transactions.
+ * @return {@code true} if network connectivity exists, {@code false} otherwise.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ */
+ @Deprecated
+ public boolean isConnected() {
+ synchronized (this) {
+ return mState == State.CONNECTED;
+ }
+ }
+
+ /**
+ * Indicates whether network connectivity is possible. A network is unavailable
+ * when a persistent or semi-persistent condition prevents the possibility
+ * of connecting to that network. Examples include
+ * <ul>
+ * <li>The device is out of the coverage area for any network of this type.</li>
+ * <li>The device is on a network other than the home network (i.e., roaming), and
+ * data roaming has been disabled.</li>
+ * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li>
+ * </ul>
+ * Since Android L, this always returns {@code true}, because the system only
+ * returns info for available networks.
+ * @return {@code true} if the network is available, {@code false} otherwise
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes.
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ */
+ @Deprecated
+ public boolean isAvailable() {
+ synchronized (this) {
+ return mIsAvailable;
+ }
+ }
+
+ /**
+ * Sets if the network is available, ie, if the connectivity is possible.
+ * @param isAvailable the new availability value.
+ * @deprecated Use {@link NetworkCapabilities} instead
+ *
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public void setIsAvailable(boolean isAvailable) {
+ synchronized (this) {
+ mIsAvailable = isAvailable;
+ }
+ }
+
+ /**
+ * Indicates whether the current attempt to connect to the network
+ * resulted from the ConnectivityManager trying to fail over to this
+ * network following a disconnect from another network.
+ * @return {@code true} if this is a failover attempt, {@code false}
+ * otherwise.
+ * @deprecated This field is not populated in recent Android releases,
+ * and does not make a lot of sense in a multi-network world.
+ */
+ @Deprecated
+ public boolean isFailover() {
+ synchronized (this) {
+ return mIsFailover;
+ }
+ }
+
+ /**
+ * Set the failover boolean.
+ * @param isFailover {@code true} to mark the current connection attempt
+ * as a failover.
+ * @deprecated This hasn't been set in any recent Android release.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public void setFailover(boolean isFailover) {
+ synchronized (this) {
+ mIsFailover = isFailover;
+ }
+ }
+
+ /**
+ * Indicates whether the device is currently roaming on this network. When
+ * {@code true}, it suggests that use of data on this network may incur
+ * extra costs.
+ *
+ * @return {@code true} if roaming is in effect, {@code false} otherwise.
+ * @deprecated Callers should switch to checking
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING}
+ * instead, since that handles more complex situations, such as
+ * VPNs.
+ */
+ @Deprecated
+ public boolean isRoaming() {
+ synchronized (this) {
+ return mIsRoaming;
+ }
+ }
+
+ /**
+ * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING} instead.
+ * {@hide}
+ */
+ @VisibleForTesting
+ @Deprecated
+ @UnsupportedAppUsage
+ public void setRoaming(boolean isRoaming) {
+ synchronized (this) {
+ mIsRoaming = isRoaming;
+ }
+ }
+
+ /**
+ * Reports the current coarse-grained state of the network.
+ * @return the coarse-grained state
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes.
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ */
+ @Deprecated
+ public State getState() {
+ synchronized (this) {
+ return mState;
+ }
+ }
+
+ /**
+ * Reports the current fine-grained state of the network.
+ * @return the fine-grained state
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
+ */
+ @Deprecated
+ public @NonNull DetailedState getDetailedState() {
+ synchronized (this) {
+ return mDetailedState;
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @Deprecated
+ public void setDetailedState(@NonNull DetailedState detailedState, @Nullable String reason,
+ @Nullable String extraInfo) {
+ synchronized (this) {
+ this.mDetailedState = detailedState;
+ this.mState = stateMap.get(detailedState);
+ this.mReason = reason;
+ this.mExtraInfo = extraInfo;
+ }
+ }
+
+ /**
+ * Set the extraInfo field.
+ * @param extraInfo an optional {@code String} providing addditional network state
+ * information passed up from the lower networking layers.
+ * @deprecated See {@link NetworkInfo#getExtraInfo}.
+ * @hide
+ */
+ @Deprecated
+ public void setExtraInfo(String extraInfo) {
+ synchronized (this) {
+ this.mExtraInfo = extraInfo;
+ }
+ }
+
+ /**
+ * Report the reason an attempt to establish connectivity failed,
+ * if one is available.
+ * @return the reason for failure, or null if not available
+ * @deprecated This method does not have a consistent contract that could make it useful
+ * to callers.
+ */
+ public String getReason() {
+ synchronized (this) {
+ return mReason;
+ }
+ }
+
+ /**
+ * Report the extra information about the network state, if any was
+ * provided by the lower networking layers.
+ * @return the extra information, or null if not available
+ * @deprecated Use other services e.g. WifiManager to get additional information passed up from
+ * the lower networking layers.
+ */
+ @Deprecated
+ public String getExtraInfo() {
+ synchronized (this) {
+ return mExtraInfo;
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ final StringBuilder builder = new StringBuilder("[");
+ builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
+ append("], state: ").append(mState).append("/").append(mDetailedState).
+ append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
+ append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
+ append(", failover: ").append(mIsFailover).
+ append(", available: ").append(mIsAvailable).
+ append(", roaming: ").append(mIsRoaming).
+ append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Returns a brief summary string suitable for debugging.
+ * @hide
+ */
+ public String toShortString() {
+ synchronized (this) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getTypeName());
+
+ final String subtype = getSubtypeName();
+ if (!TextUtils.isEmpty(subtype)) {
+ builder.append("[").append(subtype).append("]");
+ }
+
+ builder.append(" ");
+ builder.append(mDetailedState);
+ if (mIsRoaming) {
+ builder.append(" ROAMING");
+ }
+ if (mExtraInfo != null) {
+ builder.append(" extra: ").append(mExtraInfo);
+ }
+ return builder.toString();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ synchronized (this) {
+ dest.writeInt(mNetworkType);
+ dest.writeInt(mSubtype);
+ dest.writeString(mTypeName);
+ dest.writeString(mSubtypeName);
+ dest.writeString(mState.name());
+ dest.writeString(mDetailedState.name());
+ dest.writeInt(mIsFailover ? 1 : 0);
+ dest.writeInt(mIsAvailable ? 1 : 0);
+ dest.writeInt(mIsRoaming ? 1 : 0);
+ dest.writeString(mReason);
+ dest.writeString(mExtraInfo);
+ }
+ }
+
+ public static final @android.annotation.NonNull Creator<NetworkInfo> CREATOR = new Creator<NetworkInfo>() {
+ @Override
+ public NetworkInfo createFromParcel(Parcel in) {
+ int netType = in.readInt();
+ int subtype = in.readInt();
+ String typeName = in.readString();
+ String subtypeName = in.readString();
+ NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName);
+ netInfo.mState = State.valueOf(in.readString());
+ netInfo.mDetailedState = DetailedState.valueOf(in.readString());
+ netInfo.mIsFailover = in.readInt() != 0;
+ netInfo.mIsAvailable = in.readInt() != 0;
+ netInfo.mIsRoaming = in.readInt() != 0;
+ netInfo.mReason = in.readString();
+ netInfo.mExtraInfo = in.readString();
+ return netInfo;
+ }
+
+ @Override
+ public NetworkInfo[] newArray(int size) {
+ return new NetworkInfo[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/NetworkProvider.java b/framework/src/android/net/NetworkProvider.java
new file mode 100644
index 0000000..14cb51c
--- /dev/null
+++ b/framework/src/android/net/NetworkProvider.java
@@ -0,0 +1,162 @@
+/*
+ * 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 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;
+
+ /**
+ * 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 Context mContext;
+
+ 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) {
+ 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);
+ }
+ }
+ };
+ mContext = context;
+ 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) {
+ ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
+ }
+}
diff --git a/framework/src/android/net/NetworkRequest.aidl b/framework/src/android/net/NetworkRequest.aidl
new file mode 100644
index 0000000..508defc
--- /dev/null
+++ b/framework/src/android/net/NetworkRequest.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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;
+
+parcelable NetworkRequest;
+
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
new file mode 100644
index 0000000..04011fc
--- /dev/null
+++ b/framework/src/android/net/NetworkRequest.java
@@ -0,0 +1,582 @@
+/*
+ * 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.RequiresPermission;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkCapabilities.Transport;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
+ * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
+ * via {@link ConnectivityManager#registerNetworkCallback}.
+ */
+public class NetworkRequest implements Parcelable {
+ /**
+ * The first requestId value that will be allocated.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int FIRST_REQUEST_ID = 1;
+
+ /**
+ * The requestId value that represents the absence of a request.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int REQUEST_ID_NONE = -1;
+
+ /**
+ * The {@link NetworkCapabilities} that define this request.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public final @NonNull NetworkCapabilities networkCapabilities;
+
+ /**
+ * Identifies the request. NetworkRequests should only be constructed by
+ * the Framework and given out to applications as tokens to be used to identify
+ * the request.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public final int requestId;
+
+ /**
+ * Set for legacy requests and the default. Set to TYPE_NONE for none.
+ * Causes CONNECTIVITY_ACTION broadcasts to be sent.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ public final int legacyType;
+
+ /**
+ * A NetworkRequest as used by the system can be one of the following types:
+ *
+ * - LISTEN, for which the framework will issue callbacks about any
+ * and all networks that match the specified NetworkCapabilities,
+ *
+ * - REQUEST, capable of causing a specific network to be created
+ * first (e.g. a telephony DUN request), the framework will issue
+ * callbacks about the single, highest scoring current network
+ * (if any) that matches the specified NetworkCapabilities, or
+ *
+ * - TRACK_DEFAULT, a hybrid of the two designed such that the
+ * framework will issue callbacks for the single, highest scoring
+ * current network (if any) that matches the capabilities of the
+ * default Internet request (mDefaultRequest), but which cannot cause
+ * the framework to either create or retain the existence of any
+ * specific network. Note that from the point of view of the request
+ * matching code, TRACK_DEFAULT is identical to REQUEST: its special
+ * behaviour is not due to different semantics, but to the fact that
+ * the system will only ever create a TRACK_DEFAULT with capabilities
+ * that are identical to the default request's capabilities, thus
+ * causing it to share fate in every way with the default request.
+ *
+ * - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
+ * to retain the NET_CAPABILITY_FOREGROUND capability. A network with
+ * no foreground requests is in the background. A network that has
+ * one or more background requests and loses its last foreground
+ * request to a higher-scoring network will not go into the
+ * background immediately, but will linger and go into the background
+ * after the linger timeout.
+ *
+ * - The value NONE is used only by applications. When an application
+ * creates a NetworkRequest, it does not have a type; the type is set
+ * by the system depending on the method used to file the request
+ * (requestNetwork, registerNetworkCallback, etc.).
+ *
+ * @hide
+ */
+ public static enum Type {
+ NONE,
+ LISTEN,
+ TRACK_DEFAULT,
+ REQUEST,
+ BACKGROUND_REQUEST,
+ };
+
+ /**
+ * The type of the request. This is only used by the system and is always NONE elsewhere.
+ *
+ * @hide
+ */
+ public final Type type;
+
+ /**
+ * @hide
+ */
+ public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId, Type type) {
+ if (nc == null) {
+ throw new NullPointerException();
+ }
+ requestId = rId;
+ networkCapabilities = nc;
+ this.legacyType = legacyType;
+ this.type = type;
+ }
+
+ /**
+ * @hide
+ */
+ public NetworkRequest(NetworkRequest that) {
+ networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
+ requestId = that.requestId;
+ this.legacyType = that.legacyType;
+ this.type = that.type;
+ }
+
+ /**
+ * Builder used to create {@link NetworkRequest} objects. Specify the Network features
+ * needed in terms of {@link NetworkCapabilities} features
+ */
+ public static class Builder {
+ private final NetworkCapabilities mNetworkCapabilities;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {
+ // By default, restrict this request to networks available to this app.
+ // Apps can rescind this restriction, but ConnectivityService will enforce
+ // it for apps that do not have the NETWORK_SETTINGS permission.
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.setSingleUid(Process.myUid());
+ }
+
+ /**
+ * Build {@link NetworkRequest} give the current set of capabilities.
+ */
+ public NetworkRequest build() {
+ // Make a copy of mNetworkCapabilities so we don't inadvertently remove NOT_RESTRICTED
+ // when later an unrestricted capability could be added to mNetworkCapabilities, in
+ // which case NOT_RESTRICTED should be returned to mNetworkCapabilities, which
+ // maybeMarkCapabilitiesRestricted() doesn't add back.
+ final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
+ nc.maybeMarkCapabilitiesRestricted();
+ return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
+ ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
+ }
+
+ /**
+ * Add the given capability requirement to this builder. These represent
+ * the requested network's required capabilities. Note that when searching
+ * for a network to satisfy a request, all capabilities requested must be
+ * satisfied.
+ *
+ * @param capability The capability to add.
+ * @return The builder to facilitate chaining
+ * {@code builder.addCapability(...).addCapability();}.
+ */
+ public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
+ mNetworkCapabilities.addCapability(capability);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given capability from this builder instance.
+ *
+ * @param capability The capability to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ return this;
+ }
+
+ /**
+ * Set the {@code NetworkCapabilities} for this builder instance,
+ * overriding any capabilities that had been previously set.
+ *
+ * @param nc The superseding {@code NetworkCapabilities} instance.
+ * @return The builder to facilitate chaining.
+ * @hide
+ */
+ public Builder setCapabilities(NetworkCapabilities nc) {
+ mNetworkCapabilities.set(nc);
+ return this;
+ }
+
+ /**
+ * Set the watched UIDs for this request. This will be reset and wiped out unless
+ * the calling app holds the CHANGE_NETWORK_STATE permission.
+ *
+ * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+ * @return The builder to facilitate chaining.
+ * @hide
+ */
+ public Builder setUids(Set<UidRange> uids) {
+ mNetworkCapabilities.setUids(uids);
+ return this;
+ }
+
+ /**
+ * Add a capability that must not exist in the requested network.
+ * <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
+ * it will be removed from the list of required capabilities as well.
+ *
+ * @see #addCapability(int)
+ *
+ * @param capability The capability to add to unwanted capability list.
+ * @return The builder to facilitate chaining.
+ *
+ * @hide
+ */
+ public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
+ mNetworkCapabilities.addUnwantedCapability(capability);
+ return this;
+ }
+
+ /**
+ * Completely clears all the {@code NetworkCapabilities} from this builder instance,
+ * removing even the capabilities that are set by default when the object is constructed.
+ *
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder clearCapabilities() {
+ mNetworkCapabilities.clearAll();
+ return this;
+ }
+
+ /**
+ * Adds the given transport requirement to this builder. These represent
+ * the set of allowed transports for the request. Only networks using one
+ * of these transports will satisfy the request. If no particular transports
+ * are required, none should be specified here.
+ *
+ * @param transportType The transport type to add.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addTransportType(@NetworkCapabilities.Transport int transportType) {
+ mNetworkCapabilities.addTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given transport from this builder instance.
+ *
+ * @param transportType The transport type to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeTransportType(@NetworkCapabilities.Transport int transportType) {
+ mNetworkCapabilities.removeTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setLinkUpstreamBandwidthKbps(int upKbps) {
+ mNetworkCapabilities.setLinkUpstreamBandwidthKbps(upKbps);
+ return this;
+ }
+ /**
+ * @hide
+ */
+ public Builder setLinkDownstreamBandwidthKbps(int downKbps) {
+ mNetworkCapabilities.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>
+ * 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 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) {
+ 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));
+ }
+ }
+
+ /**
+ * 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 networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
+ */
+ public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ 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 WifiManager.
+ * <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.
+ *
+ * <p>This method requires the caller to hold the
+ * {@link android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP} permission
+ *
+ * @param signalStrength the bearer-specific signal strength.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP)
+ public @NonNull Builder setSignalStrength(int signalStrength) {
+ mNetworkCapabilities.setSignalStrength(signalStrength);
+ return this;
+ }
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ networkCapabilities.writeToParcel(dest, flags);
+ dest.writeInt(legacyType);
+ dest.writeInt(requestId);
+ dest.writeString(type.name());
+ }
+
+ public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR =
+ new Creator<NetworkRequest>() {
+ public NetworkRequest createFromParcel(Parcel in) {
+ NetworkCapabilities nc = NetworkCapabilities.CREATOR.createFromParcel(in);
+ int legacyType = in.readInt();
+ int requestId = in.readInt();
+ Type type = Type.valueOf(in.readString()); // IllegalArgumentException if invalid.
+ NetworkRequest result = new NetworkRequest(nc, legacyType, requestId, type);
+ return result;
+ }
+ public NetworkRequest[] newArray(int size) {
+ return new NetworkRequest[size];
+ }
+ };
+
+ /**
+ * Returns true iff. this NetworkRequest is of type LISTEN.
+ *
+ * @hide
+ */
+ public boolean isListen() {
+ return type == Type.LISTEN;
+ }
+
+ /**
+ * Returns true iff. the contained NetworkRequest is one that:
+ *
+ * - should be associated with at most one satisfying network
+ * at a time;
+ *
+ * - should cause a network to be kept up, but not necessarily in
+ * the foreground, if it is the best network which can satisfy the
+ * NetworkRequest.
+ *
+ * For full detail of how isRequest() is used for pairing Networks with
+ * NetworkRequests read rematchNetworkAndRequests().
+ *
+ * @hide
+ */
+ public boolean isRequest() {
+ return isForegroundRequest() || isBackgroundRequest();
+ }
+
+ /**
+ * Returns true iff. the contained NetworkRequest is one that:
+ *
+ * - should be associated with at most one satisfying network
+ * at a time;
+ *
+ * - should cause a network to be kept up and in the foreground if
+ * it is the best network which can satisfy the NetworkRequest.
+ *
+ * For full detail of how isRequest() is used for pairing Networks with
+ * NetworkRequests read rematchNetworkAndRequests().
+ *
+ * @hide
+ */
+ public boolean isForegroundRequest() {
+ return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+ }
+
+ /**
+ * Returns true iff. this NetworkRequest is of type BACKGROUND_REQUEST.
+ *
+ * @hide
+ */
+ public boolean isBackgroundRequest() {
+ return type == Type.BACKGROUND_REQUEST;
+ }
+
+ /**
+ * @see Builder#addCapability(int)
+ */
+ public boolean hasCapability(@NetCapability int capability) {
+ return networkCapabilities.hasCapability(capability);
+ }
+
+ /**
+ * @see Builder#addUnwantedCapability(int)
+ *
+ * @hide
+ */
+ public boolean hasUnwantedCapability(@NetCapability int capability) {
+ return networkCapabilities.hasUnwantedCapability(capability);
+ }
+
+ /**
+ * 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 : "") +
+ ", " + networkCapabilities.toString() + " ]";
+ }
+
+ private int typeToProtoEnum(Type t) {
+ switch (t) {
+ case NONE:
+ return NetworkRequestProto.TYPE_NONE;
+ case LISTEN:
+ return NetworkRequestProto.TYPE_LISTEN;
+ case TRACK_DEFAULT:
+ return NetworkRequestProto.TYPE_TRACK_DEFAULT;
+ case REQUEST:
+ return NetworkRequestProto.TYPE_REQUEST;
+ case BACKGROUND_REQUEST:
+ return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
+ default:
+ return NetworkRequestProto.TYPE_UNKNOWN;
+ }
+ }
+
+ /** @hide */
+ 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.dumpDebug(proto, NetworkRequestProto.NETWORK_CAPABILITIES);
+
+ proto.end(token);
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkRequest == false) return false;
+ NetworkRequest that = (NetworkRequest)obj;
+ return (that.legacyType == this.legacyType &&
+ that.requestId == this.requestId &&
+ that.type == this.type &&
+ Objects.equals(that.networkCapabilities, this.networkCapabilities));
+ }
+
+ public int hashCode() {
+ return Objects.hash(requestId, legacyType, networkCapabilities, type);
+ }
+}
diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java
new file mode 100644
index 0000000..8be4af7
--- /dev/null
+++ b/framework/src/android/net/NetworkUtils.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2008 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.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.system.ErrnoException;
+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.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Locale;
+import java.util.TreeSet;
+
+/**
+ * Native methods for managing network interfaces.
+ *
+ * {@hide}
+ */
+public class NetworkUtils {
+
+ private static final String TAG = "NetworkUtils";
+
+ /**
+ * Attaches a socket filter that drops all of incoming packets.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void attachDropAllBPFFilter(FileDescriptor fd) throws SocketException;
+
+ /**
+ * Detaches a socket filter.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void detachBPFFilter(FileDescriptor fd) 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
+ * {@code Network} ever disconnects all sockets created in this way will cease to work. This
+ * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+ * a particular {@code Network}. Passing NETID_UNSET clears the binding.
+ */
+ public native static boolean bindProcessToNetwork(int netId);
+
+ /**
+ * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+ * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+ */
+ public native static int getBoundNetworkForProcess();
+
+ /**
+ * Binds host resolutions performed by this process to the network designated by {@code netId}.
+ * {@link #bindProcessToNetwork} takes precedence over this setting. Passing NETID_UNSET clears
+ * the binding.
+ *
+ * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+ */
+ @Deprecated
+ public native static boolean bindProcessToNetworkForHostResolution(int netId);
+
+ /**
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
+ * overrides any binding via {@link #bindProcessToNetwork}.
+ * @return 0 on success or negative errno on failure.
+ */
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
+
+ /**
+ * Protect {@code fd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static native boolean protectFromVpn(FileDescriptor fd);
+
+ /**
+ * Protect {@code socketfd} from VPN connections. After protecting, data sent through
+ * this socket will go directly to the underlying network, so its traffic will not be
+ * forwarded through the VPN.
+ */
+ public native static boolean protectFromVpn(int socketfd);
+
+ /**
+ * Determine if {@code uid} can access network designated by {@code netId}.
+ * @return {@code true} if {@code uid} can access network, {@code false} otherwise.
+ */
+ public native static boolean queryUserAccess(int uid, int netId);
+
+ /**
+ * DNS resolver series jni method.
+ * Issue the query {@code msg} on the network designated by {@code netId}.
+ * {@code flags} is an additional config to control actual querying behavior.
+ * @return a file descriptor to watch for read events
+ */
+ public static native FileDescriptor resNetworkSend(
+ int netId, byte[] msg, int msglen, int flags) throws ErrnoException;
+
+ /**
+ * DNS resolver series jni method.
+ * Look up the {@code nsClass} {@code nsType} Resource Record (RR) associated
+ * with Domain Name {@code dname} on the network designated by {@code netId}.
+ * {@code flags} is an additional config to control actual querying behavior.
+ * @return a file descriptor to watch for read events
+ */
+ public static native FileDescriptor resNetworkQuery(
+ int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
+
+ /**
+ * DNS resolver series jni method.
+ * Read a result for the query associated with the {@code fd}.
+ * @return DnsResponse containing blob answer and rcode
+ */
+ public static native DnsResolver.DnsResponse resNetworkResult(FileDescriptor fd)
+ throws ErrnoException;
+
+ /**
+ * DNS resolver series jni method.
+ * Attempts to cancel the in-progress query associated with the {@code fd}.
+ */
+ public static native void resNetworkCancel(FileDescriptor fd);
+
+ /**
+ * DNS resolver series jni method.
+ * Attempts to get network which resolver will use if no network is explicitly selected.
+ */
+ public static native Network getDnsNetwork() throws ErrnoException;
+
+ /**
+ * Get the tcp repair window associated with the {@code fd}.
+ *
+ * @param fd the tcp socket's {@link FileDescriptor}.
+ * @return a {@link TcpRepairWindow} object indicates tcp window size.
+ */
+ public static native TcpRepairWindow getTcpRepairWindow(FileDescriptor fd)
+ throws ErrnoException;
+
+ /**
+ * @see Inet4AddressUtils#intToInet4AddressHTL(int)
+ * @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)}
+ * or {@link Inet4AddressUtils#intToInet4AddressHTL(int)}
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static InetAddress intToInetAddress(int hostAddress) {
+ return Inet4AddressUtils.intToInet4AddressHTL(hostAddress);
+ }
+
+ /**
+ * @see Inet4AddressUtils#inet4AddressToIntHTL(Inet4Address)
+ * @deprecated Use either {@link Inet4AddressUtils#inet4AddressToIntHTH(Inet4Address)}
+ * or {@link Inet4AddressUtils#inet4AddressToIntHTL(Inet4Address)}
+ */
+ @Deprecated
+ public static int inetAddressToInt(Inet4Address inetAddr)
+ throws IllegalArgumentException {
+ return Inet4AddressUtils.inet4AddressToIntHTL(inetAddr);
+ }
+
+ /**
+ * @see Inet4AddressUtils#prefixLengthToV4NetmaskIntHTL(int)
+ * @deprecated Use either {@link Inet4AddressUtils#prefixLengthToV4NetmaskIntHTH(int)}
+ * or {@link Inet4AddressUtils#prefixLengthToV4NetmaskIntHTL(int)}
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public static int prefixLengthToNetmaskInt(int prefixLength)
+ throws IllegalArgumentException {
+ return Inet4AddressUtils.prefixLengthToV4NetmaskIntHTL(prefixLength);
+ }
+
+ /**
+ * Convert a IPv4 netmask integer to a prefix length
+ * @param netmask as an integer (0xff000000 for a /8 subnet)
+ * @return the network prefix length
+ */
+ public static int netmaskIntToPrefixLength(int netmask) {
+ return Integer.bitCount(netmask);
+ }
+
+ /**
+ * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
+ * @param netmask as a {@code Inet4Address}.
+ * @return the network prefix length
+ * @throws IllegalArgumentException the specified netmask was not contiguous.
+ * @hide
+ * @deprecated use {@link Inet4AddressUtils#netmaskToPrefixLength(Inet4Address)}
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
+ public static int netmaskToPrefixLength(Inet4Address netmask) {
+ // This is only here because some apps seem to be using it (@UnsupportedAppUsage).
+ return Inet4AddressUtils.netmaskToPrefixLength(netmask);
+ }
+
+
+ /**
+ * Create an InetAddress from a string where the string must be a standard
+ * representation of a V4 or V6 address. Avoids doing a DNS lookup on failure
+ * but it will throw an IllegalArgumentException in that case.
+ * @param addrString
+ * @return the InetAddress
+ * @hide
+ * @deprecated Use {@link InetAddresses#parseNumericAddress(String)}, if possible.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ @Deprecated
+ public static InetAddress numericToInetAddress(String addrString)
+ throws IllegalArgumentException {
+ return InetAddress.parseNumericAddress(addrString);
+ }
+
+ /**
+ * Masks a raw IP address byte array with the specified prefix length.
+ */
+ public static void maskRawAddress(byte[] array, int prefixLength) {
+ if (prefixLength < 0 || prefixLength > array.length * 8) {
+ throw new RuntimeException("IP address with " + array.length +
+ " bytes has invalid prefix length " + prefixLength);
+ }
+
+ int offset = prefixLength / 8;
+ int remainder = prefixLength % 8;
+ byte mask = (byte)(0xFF << (8 - remainder));
+
+ if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
+
+ offset++;
+
+ for (; offset < array.length; offset++) {
+ array[offset] = 0;
+ }
+ }
+
+ /**
+ * Get InetAddress masked with prefixLength. Will never return null.
+ * @param address the IP address to mask with
+ * @param prefixLength the prefixLength used to mask the IP
+ */
+ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+ byte[] array = address.getAddress();
+ maskRawAddress(array, prefixLength);
+
+ InetAddress netPart = null;
+ try {
+ netPart = InetAddress.getByAddress(array);
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("getNetworkPart error - " + e.toString());
+ }
+ return netPart;
+ }
+
+ /**
+ * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static int getImplicitNetmask(Inet4Address address) {
+ // Only here because it seems to be used by apps
+ return Inet4AddressUtils.getImplicitNetmask(address);
+ }
+
+ /**
+ * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
+ * @hide
+ */
+ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) {
+ InetAddress address = null;
+ int prefixLength = -1;
+ try {
+ String[] pieces = ipAndMaskString.split("/", 2);
+ prefixLength = Integer.parseInt(pieces[1]);
+ address = InetAddress.parseNumericAddress(pieces[0]);
+ } catch (NullPointerException e) { // Null string.
+ } catch (ArrayIndexOutOfBoundsException e) { // No prefix length.
+ } catch (NumberFormatException e) { // Non-numeric prefix.
+ } catch (IllegalArgumentException e) { // Invalid IP address.
+ }
+
+ if (address == null || prefixLength == -1) {
+ throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString);
+ }
+
+ return new Pair<InetAddress, Integer>(address, prefixLength);
+ }
+
+ /**
+ * 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
+ * @param addrHexString a 32 character hex string representing an IPv6 addr
+ * @return addr an InetAddress representation for the string
+ */
+ public static InetAddress hexToInet6Address(String addrHexString)
+ throws IllegalArgumentException {
+ try {
+ return numericToInetAddress(String.format(Locale.US, "%s:%s:%s:%s:%s:%s:%s:%s",
+ addrHexString.substring(0,4), addrHexString.substring(4,8),
+ addrHexString.substring(8,12), addrHexString.substring(12,16),
+ addrHexString.substring(16,20), addrHexString.substring(20,24),
+ addrHexString.substring(24,28), addrHexString.substring(28,32)));
+ } catch (Exception e) {
+ Log.e("NetworkUtils", "error in hexToInet6Address(" + addrHexString + "): " + e);
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * Trim leading zeros from IPv4 address strings
+ * Our base libraries will interpret that as octel..
+ * Must leave non v4 addresses and host names alone.
+ * For example, 192.168.000.010 -> 192.168.0.10
+ * TODO - fix base libraries and remove this function
+ * @param addr a string representing an ip addr
+ * @return a string propertly trimmed
+ */
+ @UnsupportedAppUsage
+ public static String trimV4AddrZeros(String addr) {
+ if (addr == null) return null;
+ String[] octets = addr.split("\\.");
+ if (octets.length != 4) return addr;
+ StringBuilder builder = new StringBuilder(16);
+ String result = null;
+ for (int i = 0; i < 4; i++) {
+ try {
+ if (octets[i].length() > 3) return addr;
+ builder.append(Integer.parseInt(octets[i]));
+ } catch (NumberFormatException e) {
+ return addr;
+ }
+ if (i < 3) builder.append('.');
+ }
+ result = builder.toString();
+ return result;
+ }
+
+ /**
+ * Returns a prefix set without overlaps.
+ *
+ * This expects the src set to be sorted from shorter to longer. Results are undefined
+ * failing this condition. The returned prefix set is sorted in the same order as the
+ * passed set, with the same comparator.
+ */
+ private static TreeSet<IpPrefix> deduplicatePrefixSet(final TreeSet<IpPrefix> src) {
+ final TreeSet<IpPrefix> dst = new TreeSet<>(src.comparator());
+ // Prefixes match addresses that share their upper part up to their length, therefore
+ // the only kind of possible overlap in two prefixes is strict inclusion of the longer
+ // (more restrictive) in the shorter (including equivalence if they have the same
+ // length).
+ // Because prefixes in the src set are sorted from shorter to longer, deduplicating
+ // is done by simply iterating in order, and not adding any longer prefix that is
+ // already covered by a shorter one.
+ newPrefixes:
+ for (IpPrefix newPrefix : src) {
+ for (IpPrefix existingPrefix : dst) {
+ if (existingPrefix.containsPrefix(newPrefix)) {
+ continue newPrefixes;
+ }
+ }
+ dst.add(newPrefix);
+ }
+ return dst;
+ }
+
+ /**
+ * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set.
+ *
+ * Obviously this returns an integral value between 0 and 2**32.
+ * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the
+ * set is not ordered smallest prefix to longer prefix.
+ *
+ * @param prefixes the set of prefixes, ordered by length
+ */
+ public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) {
+ long routedIPCount = 0;
+ for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+ if (!prefix.isIPv4()) {
+ Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount");
+ }
+ int rank = 32 - prefix.getPrefixLength();
+ routedIPCount += 1L << rank;
+ }
+ return routedIPCount;
+ }
+
+ /**
+ * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set.
+ *
+ * This returns a BigInteger between 0 and 2**128.
+ * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the
+ * set is not ordered smallest prefix to longer prefix.
+ */
+ public static BigInteger routedIPv6AddressCount(final TreeSet<IpPrefix> prefixes) {
+ BigInteger routedIPCount = BigInteger.ZERO;
+ for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+ if (!prefix.isIPv6()) {
+ Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount");
+ }
+ int rank = 128 - prefix.getPrefixLength();
+ routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank));
+ }
+ return routedIPCount;
+ }
+
+}
diff --git a/framework/src/android/net/PacProxySelector.java b/framework/src/android/net/PacProxySelector.java
new file mode 100644
index 0000000..326943a
--- /dev/null
+++ b/framework/src/android/net/PacProxySelector.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 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.ServiceManager;
+import android.util.Log;
+
+import com.android.net.IProxyService;
+
+import com.google.android.collect.Lists;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.Proxy.Type;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class PacProxySelector extends ProxySelector {
+ private static final String TAG = "PacProxySelector";
+ public static final String PROXY_SERVICE = "com.android.net.IProxyService";
+ private static final String SOCKS = "SOCKS ";
+ private static final String PROXY = "PROXY ";
+
+ private IProxyService mProxyService;
+ private final List<Proxy> mDefaultList;
+
+ public PacProxySelector() {
+ mProxyService = IProxyService.Stub.asInterface(
+ ServiceManager.getService(PROXY_SERVICE));
+ if (mProxyService == null) {
+ // Added because of b10267814 where mako is restarting.
+ Log.e(TAG, "PacProxyInstaller: no proxy service");
+ }
+ mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY);
+ }
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ if (mProxyService == null) {
+ mProxyService = IProxyService.Stub.asInterface(
+ ServiceManager.getService(PROXY_SERVICE));
+ }
+ if (mProxyService == null) {
+ Log.e(TAG, "select: no proxy service return NO_PROXY");
+ return Lists.newArrayList(java.net.Proxy.NO_PROXY);
+ }
+ String response = null;
+ String urlString;
+ try {
+ // Strip path and username/password from URI so it's not visible to PAC script. The
+ // path often contains credentials the app does not want exposed to a potentially
+ // malicious PAC script.
+ if (!"http".equalsIgnoreCase(uri.getScheme())) {
+ uri = new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), "/", null, null);
+ }
+ urlString = uri.toURL().toString();
+ } catch (URISyntaxException e) {
+ urlString = uri.getHost();
+ } catch (MalformedURLException e) {
+ urlString = uri.getHost();
+ }
+ try {
+ response = mProxyService.resolvePacFile(uri.getHost(), urlString);
+ } catch (Exception e) {
+ Log.e(TAG, "Error resolving PAC File", e);
+ }
+ if (response == null) {
+ return mDefaultList;
+ }
+
+ return parseResponse(response);
+ }
+
+ private static List<Proxy> parseResponse(String response) {
+ String[] split = response.split(";");
+ List<Proxy> ret = Lists.newArrayList();
+ for (String s : split) {
+ String trimmed = s.trim();
+ if (trimmed.equals("DIRECT")) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ } else if (trimmed.startsWith(PROXY)) {
+ Proxy proxy = proxyFromHostPort(Type.HTTP, trimmed.substring(PROXY.length()));
+ if (proxy != null) {
+ ret.add(proxy);
+ }
+ } else if (trimmed.startsWith(SOCKS)) {
+ Proxy proxy = proxyFromHostPort(Type.SOCKS, trimmed.substring(SOCKS.length()));
+ if (proxy != null) {
+ ret.add(proxy);
+ }
+ }
+ }
+ if (ret.size() == 0) {
+ ret.add(java.net.Proxy.NO_PROXY);
+ }
+ return ret;
+ }
+
+ private static Proxy proxyFromHostPort(Proxy.Type type, String hostPortString) {
+ try {
+ String[] hostPort = hostPortString.split(":");
+ String host = hostPort[0];
+ int port = Integer.parseInt(hostPort[1]);
+ return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
+ } catch (NumberFormatException|ArrayIndexOutOfBoundsException e) {
+ Log.d(TAG, "Unable to parse proxy " + hostPortString + " " + e);
+ return null;
+ }
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress address, IOException failure) {
+
+ }
+
+}
diff --git a/framework/src/android/net/Proxy.java b/framework/src/android/net/Proxy.java
new file mode 100644
index 0000000..03b07e0
--- /dev/null
+++ b/framework/src/android/net/Proxy.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2007 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.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.net.module.util.ProxyUtils;
+
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A convenience class for accessing the user and default proxy
+ * settings.
+ */
+public final class Proxy {
+
+ private static final String TAG = "Proxy";
+
+ private static final ProxySelector sDefaultProxySelector;
+
+ /**
+ * Used to notify an app that's caching the proxy that either the default
+ * connection has changed or any connection's proxy has changed. The new
+ * proxy should be queried using {@link ConnectivityManager#getDefaultProxy()}.
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+ /**
+ * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
+ * It describes the new proxy being used (as a {@link ProxyInfo} object).
+ * @deprecated Because {@code PROXY_CHANGE_ACTION} is sent whenever the proxy
+ * for any network on the system changes, applications should always use
+ * {@link ConnectivityManager#getDefaultProxy()} or
+ * {@link ConnectivityManager#getLinkProperties(Network)}.{@link LinkProperties#getHttpProxy()}
+ * to get the proxy for the Network(s) they are using.
+ */
+ @Deprecated
+ public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+
+ /** @hide */
+ public static final int PROXY_VALID = 0;
+ /** @hide */
+ public static final int PROXY_HOSTNAME_EMPTY = 1;
+ /** @hide */
+ public static final int PROXY_HOSTNAME_INVALID = 2;
+ /** @hide */
+ public static final int PROXY_PORT_EMPTY = 3;
+ /** @hide */
+ public static final int PROXY_PORT_INVALID = 4;
+ /** @hide */
+ public static final int PROXY_EXCLLIST_INVALID = 5;
+
+ private static ConnectivityManager sConnectivityManager = null;
+
+ // Hostname / IP REGEX validation
+ // Matches blank input, ips, and domain names
+ private static final String NAME_IP_REGEX =
+ "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
+
+ private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
+
+ private static final Pattern HOSTNAME_PATTERN;
+
+ private static final String EXCL_REGEX =
+ "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
+
+ private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
+
+ private static final Pattern EXCLLIST_PATTERN;
+
+ static {
+ HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
+ EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+ sDefaultProxySelector = ProxySelector.getDefault();
+ }
+
+ /**
+ * Return the proxy object to be used for the URL given as parameter.
+ * @param ctx A Context used to get the settings for the proxy host.
+ * @param url A URL to be accessed. Used to evaluate exclusion list.
+ * @return Proxy (java.net) object containing the host name. If the
+ * user did not set a hostname it returns the default host.
+ * A null value means that no host is to be used.
+ * {@hide}
+ */
+ @UnsupportedAppUsage
+ public static final java.net.Proxy getProxy(Context ctx, String url) {
+ String host = "";
+ if ((url != null) && !isLocalHost(host)) {
+ URI uri = URI.create(url);
+ ProxySelector proxySelector = ProxySelector.getDefault();
+
+ List<java.net.Proxy> proxyList = proxySelector.select(uri);
+
+ if (proxyList.size() > 0) {
+ return proxyList.get(0);
+ }
+ }
+ return java.net.Proxy.NO_PROXY;
+ }
+
+
+ /**
+ * Return the proxy host set by the user.
+ * @param ctx A Context used to get the settings for the proxy host.
+ * @return String containing the host name. If the user did not set a host
+ * name it returns the default host. A null value means that no
+ * host is to be used.
+ * @deprecated Use standard java vm proxy values to find the host, port
+ * and exclusion list. This call ignores the exclusion list.
+ */
+ @Deprecated
+ public static final String getHost(Context ctx) {
+ java.net.Proxy proxy = getProxy(ctx, null);
+ if (proxy == java.net.Proxy.NO_PROXY) return null;
+ try {
+ return ((InetSocketAddress)(proxy.address())).getHostName();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Return the proxy port set by the user.
+ * @param ctx A Context used to get the settings for the proxy port.
+ * @return The port number to use or -1 if no proxy is to be used.
+ * @deprecated Use standard java vm proxy values to find the host, port
+ * and exclusion list. This call ignores the exclusion list.
+ */
+ @Deprecated
+ public static final int getPort(Context ctx) {
+ java.net.Proxy proxy = getProxy(ctx, null);
+ if (proxy == java.net.Proxy.NO_PROXY) return -1;
+ try {
+ return ((InetSocketAddress)(proxy.address())).getPort();
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Return the default proxy host specified by the carrier.
+ * @return String containing the host name or null if there is no proxy for
+ * this carrier.
+ * @deprecated Use standard java vm proxy values to find the host, port and
+ * exclusion list. This call ignores the exclusion list and no
+ * longer reports only mobile-data apn-based proxy values.
+ */
+ @Deprecated
+ public static final String getDefaultHost() {
+ String host = System.getProperty("http.proxyHost");
+ if (TextUtils.isEmpty(host)) return null;
+ return host;
+ }
+
+ /**
+ * Return the default proxy port specified by the carrier.
+ * @return The port number to be used with the proxy host or -1 if there is
+ * no proxy for this carrier.
+ * @deprecated Use standard java vm proxy values to find the host, port and
+ * exclusion list. This call ignores the exclusion list and no
+ * longer reports only mobile-data apn-based proxy values.
+ */
+ @Deprecated
+ public static final int getDefaultPort() {
+ if (getDefaultHost() == null) return -1;
+ try {
+ return Integer.parseInt(System.getProperty("http.proxyPort"));
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+ private static final boolean isLocalHost(String host) {
+ if (host == null) {
+ return false;
+ }
+ try {
+ if (host != null) {
+ if (host.equalsIgnoreCase("localhost")) {
+ return true;
+ }
+ if (InetAddresses.parseNumericAddress(host).isLoopbackAddress()) {
+ return true;
+ }
+ }
+ } catch (IllegalArgumentException iex) {
+ }
+ return false;
+ }
+
+ /**
+ * Validate syntax of hostname, port and exclusion list entries
+ * {@hide}
+ */
+ public static int validate(String hostname, String port, String exclList) {
+ Matcher match = HOSTNAME_PATTERN.matcher(hostname);
+ Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
+
+ if (!match.matches()) return PROXY_HOSTNAME_INVALID;
+
+ if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
+
+ if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
+
+ if (port.length() > 0) {
+ if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
+ int portVal = -1;
+ try {
+ portVal = Integer.parseInt(port);
+ } catch (NumberFormatException ex) {
+ return PROXY_PORT_INVALID;
+ }
+ if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
+ }
+ return PROXY_VALID;
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static final void setHttpProxySystemProperty(ProxyInfo p) {
+ String host = null;
+ String port = null;
+ String exclList = null;
+ Uri pacFileUrl = Uri.EMPTY;
+ if (p != null) {
+ host = p.getHost();
+ port = Integer.toString(p.getPort());
+ exclList = ProxyUtils.exclusionListAsString(p.getExclusionList());
+ pacFileUrl = p.getPacFileUrl();
+ }
+ setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
+ }
+
+ /** @hide */
+ public static final void setHttpProxySystemProperty(String host, String port, String exclList,
+ Uri pacFileUrl) {
+ if (exclList != null) exclList = exclList.replace(",", "|");
+ if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
+ if (host != null) {
+ System.setProperty("http.proxyHost", host);
+ System.setProperty("https.proxyHost", host);
+ } else {
+ System.clearProperty("http.proxyHost");
+ System.clearProperty("https.proxyHost");
+ }
+ if (port != null) {
+ System.setProperty("http.proxyPort", port);
+ System.setProperty("https.proxyPort", port);
+ } else {
+ System.clearProperty("http.proxyPort");
+ System.clearProperty("https.proxyPort");
+ }
+ if (exclList != null) {
+ System.setProperty("http.nonProxyHosts", exclList);
+ System.setProperty("https.nonProxyHosts", exclList);
+ } else {
+ System.clearProperty("http.nonProxyHosts");
+ System.clearProperty("https.nonProxyHosts");
+ }
+ if (!Uri.EMPTY.equals(pacFileUrl)) {
+ ProxySelector.setDefault(new PacProxySelector());
+ } else {
+ ProxySelector.setDefault(sDefaultProxySelector);
+ }
+ }
+}
diff --git a/framework/src/android/net/ProxyInfo.aidl b/framework/src/android/net/ProxyInfo.aidl
new file mode 100644
index 0000000..a5d0c12
--- /dev/null
+++ b/framework/src/android/net/ProxyInfo.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2010 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;
+
+@JavaOnlyStableParcelable parcelable ProxyInfo;
+
diff --git a/framework/src/android/net/ProxyInfo.java b/framework/src/android/net/ProxyInfo.java
new file mode 100644
index 0000000..c9bca28
--- /dev/null
+++ b/framework/src/android/net/ProxyInfo.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2010 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.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.net.InetSocketAddress;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Describes a proxy configuration.
+ *
+ * Proxy configurations are already integrated within the {@code java.net} and
+ * Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use
+ * them automatically.
+ *
+ * Other HTTP stacks will need to obtain the proxy info from
+ * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
+ */
+public class ProxyInfo implements Parcelable {
+
+ private final String mHost;
+ private final int mPort;
+ private final String mExclusionList;
+ private final String[] mParsedExclusionList;
+ private final Uri mPacFileUrl;
+
+ /**
+ *@hide
+ */
+ public static final String LOCAL_EXCL_LIST = "";
+ /**
+ *@hide
+ */
+ public static final int LOCAL_PORT = -1;
+ /**
+ *@hide
+ */
+ public static final String LOCAL_HOST = "localhost";
+
+ /**
+ * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+ * on the specified host and port.
+ */
+ public static ProxyInfo buildDirectProxy(String host, int port) {
+ return new ProxyInfo(host, port, null);
+ }
+
+ /**
+ * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+ * on the specified host and port.
+ *
+ * The proxy will not be used to access any host in exclusion list, exclList.
+ *
+ * @param exclList Hosts to exclude using the proxy on connections for. These
+ * hosts can use wildcards such as *.example.com.
+ */
+ public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) {
+ String[] array = exclList.toArray(new String[exclList.size()]);
+ return new ProxyInfo(host, port, TextUtils.join(",", array), array);
+ }
+
+ /**
+ * Construct a {@link ProxyInfo} that will download and run the PAC script
+ * at the specified URL.
+ */
+ public static ProxyInfo buildPacProxy(Uri pacUri) {
+ return new ProxyInfo(pacUri);
+ }
+
+ /**
+ * 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
+ */
+ @UnsupportedAppUsage
+ public ProxyInfo(String host, int port, String exclList) {
+ mHost = host;
+ mPort = port;
+ mExclusionList = exclList;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
+ mPacFileUrl = Uri.EMPTY;
+ }
+
+ /**
+ * Create a ProxyProperties that points at a PAC URL.
+ * @hide
+ */
+ public ProxyInfo(@NonNull Uri pacFileUrl) {
+ mHost = LOCAL_HOST;
+ mPort = LOCAL_PORT;
+ mExclusionList = LOCAL_EXCL_LIST;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
+ mPacFileUrl = pacFileUrl;
+ }
+
+ /**
+ * Only used in PacProxyInstaller after Local Proxy is bound.
+ * @hide
+ */
+ public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) {
+ mHost = LOCAL_HOST;
+ mPort = localProxyPort;
+ mExclusionList = LOCAL_EXCL_LIST;
+ mParsedExclusionList = parseExclusionList(mExclusionList);
+ if (pacFileUrl == null) {
+ throw new NullPointerException();
+ }
+ mPacFileUrl = pacFileUrl;
+ }
+
+ private static String[] parseExclusionList(String exclusionList) {
+ if (exclusionList == null) {
+ return new String[0];
+ } else {
+ return exclusionList.toLowerCase(Locale.ROOT).split(",");
+ }
+ }
+
+ private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
+ mHost = host;
+ mPort = port;
+ mExclusionList = exclList;
+ mParsedExclusionList = parsedExclList;
+ mPacFileUrl = Uri.EMPTY;
+ }
+
+ /**
+ * A copy constructor to hold proxy properties.
+ */
+ public ProxyInfo(@Nullable ProxyInfo source) {
+ if (source != null) {
+ mHost = source.getHost();
+ mPort = source.getPort();
+ mPacFileUrl = source.mPacFileUrl;
+ mExclusionList = source.getExclusionListAsString();
+ mParsedExclusionList = source.mParsedExclusionList;
+ } else {
+ mHost = null;
+ mPort = 0;
+ mExclusionList = null;
+ mParsedExclusionList = null;
+ mPacFileUrl = Uri.EMPTY;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public InetSocketAddress getSocketAddress() {
+ InetSocketAddress inetSocketAddress = null;
+ try {
+ inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ } catch (IllegalArgumentException e) { }
+ return inetSocketAddress;
+ }
+
+ /**
+ * Returns the URL of the current PAC script or null if there is
+ * no PAC script.
+ */
+ public Uri getPacFileUrl() {
+ return mPacFileUrl;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the host
+ * of the proxy.
+ */
+ public String getHost() {
+ return mHost;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the port
+ * of the proxy
+ */
+ public int getPort() {
+ return mPort;
+ }
+
+ /**
+ * When configured to use a Direct Proxy this returns the list
+ * of hosts for which the proxy is ignored.
+ */
+ public String[] getExclusionList() {
+ return mParsedExclusionList;
+ }
+
+ /**
+ * comma separated
+ * @hide
+ */
+ @Nullable
+ public String getExclusionListAsString() {
+ return mExclusionList;
+ }
+
+ /**
+ * Return true if the pattern of proxy is valid, otherwise return false.
+ */
+ public boolean isValid() {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
+ return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ mPort == 0 ? "" : Integer.toString(mPort),
+ mExclusionList == null ? "" : mExclusionList);
+ }
+
+ /**
+ * @hide
+ */
+ public java.net.Proxy makeProxy() {
+ java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
+ if (mHost != null) {
+ try {
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(mHost, mPort);
+ proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, inetSocketAddress);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ return proxy;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ sb.append("PAC Script: ");
+ sb.append(mPacFileUrl);
+ }
+ if (mHost != null) {
+ sb.append("[");
+ sb.append(mHost);
+ sb.append("] ");
+ sb.append(Integer.toString(mPort));
+ if (mExclusionList != null) {
+ sb.append(" xl=").append(mExclusionList);
+ }
+ } else {
+ sb.append("[ProxyProperties.mHost == null]");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProxyInfo)) return false;
+ ProxyInfo p = (ProxyInfo)o;
+ // If PAC URL is present in either then they must be equal.
+ // Other parameters will only be for fall back.
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
+ }
+ if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
+ return false;
+ }
+ if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
+ return false;
+ }
+ if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
+ return false;
+ }
+ if (mHost != null && p.mHost == null) return false;
+ if (mHost == null && p.mHost != null) return false;
+ if (mPort != p.mPort) return false;
+ return true;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ * @hide
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ /*
+ * generate hashcode based on significant fields
+ */
+ public int hashCode() {
+ return ((null == mHost) ? 0 : mHost.hashCode())
+ + ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
+ + mPort;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ if (!Uri.EMPTY.equals(mPacFileUrl)) {
+ dest.writeByte((byte)1);
+ mPacFileUrl.writeToParcel(dest, 0);
+ dest.writeInt(mPort);
+ return;
+ } else {
+ dest.writeByte((byte)0);
+ }
+ if (mHost != null) {
+ dest.writeByte((byte)1);
+ dest.writeString(mHost);
+ dest.writeInt(mPort);
+ } else {
+ dest.writeByte((byte)0);
+ }
+ dest.writeString(mExclusionList);
+ dest.writeStringArray(mParsedExclusionList);
+ }
+
+ public static final @android.annotation.NonNull Creator<ProxyInfo> CREATOR =
+ new Creator<ProxyInfo>() {
+ public ProxyInfo createFromParcel(Parcel in) {
+ String host = null;
+ int port = 0;
+ if (in.readByte() != 0) {
+ Uri url = Uri.CREATOR.createFromParcel(in);
+ int localPort = in.readInt();
+ return new ProxyInfo(url, localPort);
+ }
+ if (in.readByte() != 0) {
+ host = in.readString();
+ port = in.readInt();
+ }
+ String exclList = in.readString();
+ String[] parsedExclList = in.createStringArray();
+ ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
+ return proxyProperties;
+ }
+
+ public ProxyInfo[] newArray(int size) {
+ return new ProxyInfo[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/RouteInfo.aidl b/framework/src/android/net/RouteInfo.aidl
new file mode 100644
index 0000000..7af9fda
--- /dev/null
+++ b/framework/src/android/net/RouteInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 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;
+
+@JavaOnlyStableParcelable parcelable RouteInfo;
diff --git a/framework/src/android/net/RouteInfo.java b/framework/src/android/net/RouteInfo.java
new file mode 100644
index 0000000..94f849f
--- /dev/null
+++ b/framework/src/android/net/RouteInfo.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2011 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.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.net.module.util.NetUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Represents a network route.
+ * <p>
+ * This is used both to describe static network configuration and live network
+ * configuration information.
+ *
+ * A route contains three pieces of information:
+ * <ul>
+ * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route.
+ * If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6)
+ * implied by the gateway IP address.
+ * <li>a gateway {@link InetAddress} indicating the next hop to use. If this is {@code null} it
+ * indicates a directly-connected route.
+ * <li>an interface (which may be unspecified).
+ * </ul>
+ * Either the destination or the gateway may be {@code null}, but not both. If the
+ * destination and gateway are both specified, they must be of the same address family
+ * (IPv4 or IPv6).
+ */
+public final class RouteInfo implements Parcelable {
+ /** @hide */
+ @IntDef(value = {
+ RTN_UNICAST,
+ RTN_UNREACHABLE,
+ RTN_THROW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RouteType {}
+
+ /**
+ * The IP destination address for this route.
+ */
+ @NonNull
+ private final IpPrefix mDestination;
+
+ /**
+ * The gateway address for this route.
+ */
+ @UnsupportedAppUsage
+ @Nullable
+ private final InetAddress mGateway;
+
+ /**
+ * The interface for this route.
+ */
+ @Nullable
+ private final String mInterface;
+
+
+ /** Unicast route. @hide */
+ @SystemApi
+ public static final int RTN_UNICAST = 1;
+
+ /** Unreachable route. @hide */
+ @SystemApi
+ public static final int RTN_UNREACHABLE = 7;
+
+ /** Throw route. @hide */
+ @SystemApi
+ public static final int RTN_THROW = 9;
+
+ /**
+ * The type of this route; one of the RTN_xxx constants above.
+ */
+ 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)
+ private final boolean mIsHost;
+ private final boolean mHasGateway;
+
+ /**
+ * 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
+ *
+ * @hide
+ */
+ @SystemApi
+ 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:
+ case RTN_THROW:
+ // TODO: It would be nice to ensure that route types that don't have nexthops or
+ // interfaces, such as unreachable or throw, can't be created if an interface or
+ // a gateway is specified. This is a bit too complicated to do at the moment
+ // because:
+ //
+ // - LinkProperties sets the interface on routes added to it, and modifies the
+ // interfaces of all the routes when its interface name changes.
+ // - Even when the gateway is null, we store a non-null gateway here.
+ //
+ // For now, we just rely on the code that sets routes to do things properly.
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown route type " + type);
+ }
+
+ if (destination == null) {
+ if (gateway != null) {
+ if (gateway instanceof Inet4Address) {
+ destination = new IpPrefix(Inet4Address.ANY, 0);
+ } else {
+ destination = new IpPrefix(Inet6Address.ANY, 0);
+ }
+ } else {
+ // no destination, no gateway. invalid.
+ throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," +
+ destination);
+ }
+ }
+ // TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and
+ // matches the documented behaviour. Before we can do this we need to fix all callers (e.g.,
+ // ConnectivityService) to stop doing things like r.getGateway().equals(), ... .
+ if (gateway == null) {
+ if (destination.getAddress() instanceof Inet4Address) {
+ gateway = Inet4Address.ANY;
+ } else {
+ gateway = Inet6Address.ANY;
+ }
+ }
+ mHasGateway = (!gateway.isAnyLocalAddress());
+
+ 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.
+ mGateway = gateway; // InetAddress objects are immutable.
+ mInterface = iface; // Strings are immutable.
+ mType = type;
+ mIsHost = isHost();
+ mMtu = mtu;
+ }
+
+ /**
+ * Constructs a {@code 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 address and prefix in an {@link IpPrefix}
+ * @param gateway the {@link InetAddress} to route packets through
+ * @param iface the interface name to send packets on
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
+ this(destination, gateway, iface, RTN_UNICAST);
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
+ this(destination == null ? null :
+ new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
+ gateway, iface);
+ }
+
+ /**
+ * Constructs a {@code 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 address and prefix in an {@link IpPrefix}
+ * @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
+ */
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) {
+ this(destination, gateway, null);
+ }
+
+ /**
+ * @hide
+ *
+ * TODO: Remove this.
+ */
+ @UnsupportedAppUsage
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) {
+ this(destination, gateway, null);
+ }
+
+ /**
+ * Constructs a default {@code RouteInfo} object.
+ *
+ * @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public RouteInfo(@NonNull InetAddress gateway) {
+ this((IpPrefix) null, gateway, null);
+ }
+
+ /**
+ * Constructs a {@code RouteInfo} object representing a direct connected subnet.
+ *
+ * @param destination the {@link IpPrefix} describing the address and prefix
+ * length of the subnet.
+ *
+ * @hide
+ */
+ public RouteInfo(@NonNull IpPrefix destination) {
+ this(destination, null, null);
+ }
+
+ /**
+ * @hide
+ */
+ public RouteInfo(@NonNull LinkAddress destination) {
+ this(destination, null, null);
+ }
+
+ /**
+ * @hide
+ */
+ public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) {
+ this(destination, null, null, type);
+ }
+
+ /**
+ * @hide
+ */
+ public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) {
+ return makeHostRoute(host, null, iface);
+ }
+
+ /**
+ * @hide
+ */
+ public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway,
+ @Nullable String iface) {
+ if (host == null) return null;
+
+ if (host instanceof Inet4Address) {
+ return new RouteInfo(new IpPrefix(host, 32), gateway, iface);
+ } else {
+ return new RouteInfo(new IpPrefix(host, 128), gateway, iface);
+ }
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private boolean isHost() {
+ return (mDestination.getAddress() instanceof Inet4Address &&
+ mDestination.getPrefixLength() == 32) ||
+ (mDestination.getAddress() instanceof Inet6Address &&
+ mDestination.getPrefixLength() == 128);
+ }
+
+ /**
+ * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
+ *
+ * @return {@link IpPrefix} specifying the destination. This is never {@code null}.
+ */
+ @NonNull
+ public IpPrefix getDestination() {
+ return mDestination;
+ }
+
+ /**
+ * TODO: Convert callers to use IpPrefix and then remove.
+ * @hide
+ */
+ @NonNull
+ public LinkAddress getDestinationLinkAddress() {
+ return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength());
+ }
+
+ /**
+ * Retrieves the gateway or next hop {@link InetAddress} for this route.
+ *
+ * @return {@link InetAddress} specifying the gateway or next hop. This may be
+ * {@code null} for a directly-connected route."
+ */
+ @Nullable
+ public InetAddress getGateway() {
+ return mGateway;
+ }
+
+ /**
+ * Retrieves the interface used for this route if specified, else {@code null}.
+ *
+ * @return The name of the interface used for this route.
+ */
+ @Nullable
+ public String getInterface() {
+ return mInterface;
+ }
+
+ /**
+ * Retrieves the type of this route.
+ *
+ * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RouteType
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean isDefaultRoute() {
+ return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0;
+ }
+
+ /**
+ * 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
+ */
+ public boolean isIPv4Default() {
+ return isDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
+ }
+
+ /**
+ * 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
+ */
+ public boolean isIPv6Default() {
+ return isDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
+ }
+
+ /**
+ * 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,
+ * respectively.
+ * @hide
+ */
+ public boolean isHostRoute() {
+ return mIsHost;
+ }
+
+ /**
+ * Indicates if this route has a next hop ({@code true}) or is directly-connected
+ * ({@code false}).
+ *
+ * @return {@code true} if a gateway is specified
+ */
+ public boolean hasGateway() {
+ return mHasGateway;
+ }
+
+ /**
+ * Determines whether the destination and prefix of this route includes the specified
+ * address.
+ *
+ * @param destination A {@link InetAddress} to test to see if it would match this route.
+ * @return {@code true} if the destination and prefix length cover the given address.
+ */
+ public boolean matches(InetAddress destination) {
+ return mDestination.contains(destination);
+ }
+
+ /**
+ * Find the route from a Collection of routes that best matches a given address.
+ * May return null if no routes are applicable.
+ * @param routes a Collection of RouteInfos to chose from
+ * @param dest the InetAddress your trying to get to
+ * @return the RouteInfo from the Collection that best fits the given address
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Nullable
+ public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
+ return NetUtils.selectBestRoute(routes, dest);
+ }
+
+ /**
+ * Returns a human-readable description of this object.
+ */
+ public String toString() {
+ String val = "";
+ if (mDestination != null) val = mDestination.toString();
+ if (mType == RTN_UNREACHABLE) {
+ val += " unreachable";
+ } else if (mType == RTN_THROW) {
+ val += " throw";
+ } else {
+ val += " ->";
+ if (mGateway != null) val += " " + mGateway.getHostAddress();
+ if (mInterface != null) val += " " + mInterface;
+ if (mType != RTN_UNICAST) {
+ val += " unknown type " + mType;
+ }
+ }
+ val += " mtu " + mMtu;
+ return val;
+ }
+
+ /**
+ * Compares this RouteInfo object against the specified object and indicates if they are equal.
+ * @return {@code true} if the objects are equal, {@code false} otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof RouteInfo)) return false;
+
+ RouteInfo target = (RouteInfo) obj;
+
+ return Objects.equals(mDestination, target.getDestination()) &&
+ Objects.equals(mGateway, target.getGateway()) &&
+ Objects.equals(mInterface, target.getInterface()) &&
+ 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);
+ }
+
+ /**
+ * Returns a hashcode for this <code>RouteInfo</code> object.
+ */
+ public int hashCode() {
+ return (mDestination.hashCode() * 41)
+ + (mGateway == null ? 0 :mGateway.hashCode() * 47)
+ + (mInterface == null ? 0 :mInterface.hashCode() * 67)
+ + (mType * 71) + (mMtu * 89);
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mDestination, flags);
+ byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
+ dest.writeByteArray(gatewayBytes);
+ dest.writeString(mInterface);
+ dest.writeInt(mType);
+ dest.writeInt(mMtu);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final @android.annotation.NonNull Creator<RouteInfo> CREATOR =
+ new Creator<RouteInfo>() {
+ public RouteInfo createFromParcel(Parcel in) {
+ IpPrefix dest = in.readParcelable(null);
+
+ InetAddress gateway = null;
+ byte[] addr = in.createByteArray();
+ try {
+ gateway = InetAddress.getByAddress(addr);
+ } catch (UnknownHostException e) {}
+
+ String iface = in.readString();
+ int type = in.readInt();
+ int mtu = in.readInt();
+
+ return new RouteInfo(dest, gateway, iface, type, mtu);
+ }
+
+ public RouteInfo[] newArray(int size) {
+ return new RouteInfo[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/SocketKeepalive.java b/framework/src/android/net/SocketKeepalive.java
new file mode 100644
index 0000000..d007a95
--- /dev/null
+++ b/framework/src/android/net/SocketKeepalive.java
@@ -0,0 +1,301 @@
+/*
+ * 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.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;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows applications to request that the system periodically send specific packets on their
+ * behalf, using hardware offload to save battery power.
+ *
+ * To request that the system send keepalives, call one of the methods that return a
+ * {@link SocketKeepalive} object, such as {@link ConnectivityManager#createSocketKeepalive},
+ * passing in a non-null callback. If the {@link SocketKeepalive} is successfully
+ * started, the callback's {@code onStarted} method will be called. If an error occurs,
+ * {@code onError} will be called, specifying one of the {@code ERROR_*} constants in this
+ * class.
+ *
+ * To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
+ * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
+ * {@link SocketKeepalive.Callback#onError} if an error occurred.
+ *
+ * For cellular, the device MUST support at least 1 keepalive slot.
+ *
+ * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
+ * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
+ * request. If it does, it MUST support at least 3 concurrent keepalive slots.
+ */
+public abstract class SocketKeepalive implements AutoCloseable {
+ static final String TAG = "SocketKeepalive";
+
+ /**
+ * No errors.
+ * @hide
+ */
+ @SystemApi
+ public static final int SUCCESS = 0;
+
+ /** @hide */
+ public static final int NO_KEEPALIVE = -1;
+
+ /** @hide */
+ public static final int DATA_RECEIVED = -2;
+
+ /** @hide */
+ public static final int BINDER_DIED = -10;
+
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = -20;
+ /** The specified IP addresses are invalid. For example, the specified source IP address is
+ * not configured on the specified {@code Network}. */
+ public static final int ERROR_INVALID_IP_ADDRESS = -21;
+ /** The requested port is invalid. */
+ public static final int ERROR_INVALID_PORT = -22;
+ /** The packet length is invalid (e.g., too long). */
+ public static final int ERROR_INVALID_LENGTH = -23;
+ /** The packet transmission interval is invalid (e.g., too short). */
+ public static final int ERROR_INVALID_INTERVAL = -24;
+ /** The target socket is invalid. */
+ public static final int ERROR_INVALID_SOCKET = -25;
+ /** The target socket is not idle. */
+ public static final int ERROR_SOCKET_NOT_IDLE = -26;
+ /**
+ * The stop reason is uninitialized. This should only be internally used as initial state
+ * of stop reason, instead of propagating to application.
+ * @hide
+ */
+ public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
+
+ /** The device does not support this request. */
+ public static final int ERROR_UNSUPPORTED = -30;
+ /** @hide TODO: delete when telephony code has been updated. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED;
+ /** The hardware returned an error. */
+ public static final int ERROR_HARDWARE_ERROR = -31;
+ /** The limitation of resource is reached. */
+ public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_INVALID_NETWORK,
+ ERROR_INVALID_IP_ADDRESS,
+ ERROR_INVALID_PORT,
+ ERROR_INVALID_LENGTH,
+ ERROR_INVALID_INTERVAL,
+ ERROR_INVALID_SOCKET,
+ ERROR_SOCKET_NOT_IDLE
+ })
+ 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.
+ *
+ * @hide
+ **/
+ public static final int MIN_INTERVAL_SEC = 10;
+
+ /**
+ * The maximum interval in seconds between keepalive packet transmissions.
+ *
+ * @hide
+ **/
+ public static final int MAX_INTERVAL_SEC = 3600;
+
+ /**
+ * An exception that embarks an error code.
+ * @hide
+ */
+ public static class ErrorCodeException extends Exception {
+ public final int error;
+ public ErrorCodeException(final int error, final Throwable e) {
+ super(e);
+ this.error = error;
+ }
+ public ErrorCodeException(final int error) {
+ this.error = error;
+ }
+ }
+
+ /**
+ * This socket is invalid.
+ * See the error code for details, and the optional cause.
+ * @hide
+ */
+ public static class InvalidSocketException extends ErrorCodeException {
+ public InvalidSocketException(final int error, final Throwable e) {
+ super(error, e);
+ }
+ public InvalidSocketException(final int error) {
+ super(error);
+ }
+ }
+
+ @NonNull final IConnectivityManager mService;
+ @NonNull final Network mNetwork;
+ @NonNull final ParcelFileDescriptor mPfd;
+ @NonNull final Executor mExecutor;
+ @NonNull final ISocketKeepaliveCallback mCallback;
+ // TODO: remove slot since mCallback could be used to identify which keepalive to stop.
+ @Nullable Integer mSlot;
+
+ SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
+ @NonNull ParcelFileDescriptor pfd,
+ @NonNull Executor executor, @NonNull Callback callback) {
+ mService = service;
+ mNetwork = network;
+ mPfd = pfd;
+ mExecutor = executor;
+ mCallback = new ISocketKeepaliveCallback.Stub() {
+ @Override
+ public void onStarted(int slot) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ mSlot = slot;
+ callback.onStarted();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onStopped();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onError(int error) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onError(error);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onDataReceived() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ mSlot = null;
+ callback.onDataReceived();
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+ }
+
+ /**
+ * Request that keepalive be started with the given {@code intervalSec}. See
+ * {@link SocketKeepalive}. If the remote binder dies, or the binder call throws an exception
+ * when invoking start or stop of the {@link SocketKeepalive}, a {@link RemoteException} will be
+ * thrown into the {@code executor}. This is typically not important to catch because the remote
+ * party is the system, so if it is not in shape to communicate through binder the system is
+ * probably going down anyway. If the caller cares regardless, it can use a custom
+ * {@link Executor} to catch the {@link RemoteException}.
+ *
+ * @param intervalSec The target interval in seconds between keepalive packet transmissions.
+ * The interval should be between 10 seconds and 3600 seconds, otherwise
+ * {@link #ERROR_INVALID_INTERVAL} will be returned.
+ */
+ public final void start(@IntRange(from = MIN_INTERVAL_SEC, to = MAX_INTERVAL_SEC)
+ int intervalSec) {
+ startImpl(intervalSec);
+ }
+
+ abstract void startImpl(int intervalSec);
+
+ /**
+ * Requests that keepalive be stopped. The application must wait for {@link Callback#onStopped}
+ * before using the object. See {@link SocketKeepalive}.
+ */
+ public final void stop() {
+ stopImpl();
+ }
+
+ abstract void stopImpl();
+
+ /**
+ * Deactivate this {@link SocketKeepalive} and free allocated resources. The instance won't be
+ * usable again if {@code close()} is called.
+ */
+ @Override
+ public final void close() {
+ stop();
+ try {
+ mPfd.close();
+ } catch (IOException e) {
+ // Nothing much can be done.
+ }
+ }
+
+ /**
+ * The callback which app can use to learn the status changes of {@link SocketKeepalive}. See
+ * {@link SocketKeepalive}.
+ */
+ public static class Callback {
+ /** The requested keepalive was successfully started. */
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ public void onStopped() {}
+ /** An error occurred. */
+ public void onError(@ErrorCode int error) {}
+ /** The keepalive on a TCP socket was stopped because the socket received data. This is
+ * never called for UDP sockets. */
+ public void onDataReceived() {}
+ }
+}
diff --git a/framework/src/android/net/StaticIpConfiguration.aidl b/framework/src/android/net/StaticIpConfiguration.aidl
new file mode 100644
index 0000000..8aac701
--- /dev/null
+++ b/framework/src/android/net/StaticIpConfiguration.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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;
+
+@JavaOnlyStableParcelable parcelable StaticIpConfiguration;
\ No newline at end of file
diff --git a/framework/src/android/net/StaticIpConfiguration.java b/framework/src/android/net/StaticIpConfiguration.java
new file mode 100644
index 0000000..ce54597
--- /dev/null
+++ b/framework/src/android/net/StaticIpConfiguration.java
@@ -0,0 +1,332 @@
+/*
+ * 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.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+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;
+import java.util.Objects;
+
+/**
+ * Class that describes static IP configuration.
+ *
+ * <p>This class is different from {@link LinkProperties} because it represents
+ * configuration intent. The general contract is that if we can represent
+ * a configuration here, then we should be able to configure it on a network.
+ * The intent is that it closely match the UI we have for configuring networks.
+ *
+ * <p>In contrast, {@link LinkProperties} represents current state. It is much more
+ * expressive. For example, it supports multiple IP addresses, multiple routes,
+ * stacked interfaces, and so on. Because LinkProperties is so expressive,
+ * using it to represent configuration intent as well as current state causes
+ * problems. For example, we could unknowingly save a configuration that we are
+ * not in fact capable of applying, or we could save a configuration that the
+ * UI cannot display, which has the potential for malicious code to hide
+ * hostile or unexpected configuration from the user.
+ *
+ * @hide
+ */
+@SystemApi
+public final class StaticIpConfiguration implements Parcelable {
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Nullable
+ public LinkAddress ipAddress;
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Nullable
+ public InetAddress gateway;
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @NonNull
+ public final ArrayList<InetAddress> dnsServers;
+ /** @hide */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Nullable
+ public String domains;
+
+ public StaticIpConfiguration() {
+ dnsServers = new ArrayList<>();
+ }
+
+ public StaticIpConfiguration(@Nullable StaticIpConfiguration source) {
+ this();
+ if (source != null) {
+ // All of these except dnsServers are immutable, so no need to make copies.
+ ipAddress = source.ipAddress;
+ gateway = source.gateway;
+ dnsServers.addAll(source.dnsServers);
+ domains = source.domains;
+ }
+ }
+
+ public void clear() {
+ ipAddress = null;
+ gateway = null;
+ dnsServers.clear();
+ domains = null;
+ }
+
+ /**
+ * Get the static IP address included in the configuration.
+ */
+ public @Nullable LinkAddress getIpAddress() {
+ return ipAddress;
+ }
+
+ /**
+ * Get the gateway included in the configuration.
+ */
+ public @Nullable InetAddress getGateway() {
+ return gateway;
+ }
+
+ /**
+ * Get the DNS servers included in the configuration.
+ */
+ public @NonNull List<InetAddress> getDnsServers() {
+ return dnsServers;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Helper class to build a new instance of {@link StaticIpConfiguration}.
+ */
+ public static final class Builder {
+ private LinkAddress mIpAddress;
+ private InetAddress mGateway;
+ private Iterable<InetAddress> mDnsServers;
+ private String mDomains;
+
+ /**
+ * Set the IP address to be included in the configuration; null by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setIpAddress(@Nullable LinkAddress ipAddress) {
+ mIpAddress = ipAddress;
+ return this;
+ }
+
+ /**
+ * Set the address of the gateway to be included in the configuration; null by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setGateway(@Nullable InetAddress gateway) {
+ mGateway = gateway;
+ return this;
+ }
+
+ /**
+ * Set the addresses of the DNS servers included in the configuration; empty by default.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setDnsServers(@NonNull Iterable<InetAddress> dnsServers) {
+ Preconditions.checkNotNull(dnsServers);
+ mDnsServers = dnsServers;
+ return this;
+ }
+
+ /**
+ * Sets the DNS domain search path to be used on the link; null by default.
+ * @param newDomains A {@link String} containing the comma separated domains to search when
+ * resolving host names on this link, in priority order.
+ * @return The {@link Builder} for chaining.
+ */
+ public @NonNull Builder setDomains(@Nullable String newDomains) {
+ mDomains = newDomains;
+ return this;
+ }
+
+ /**
+ * Create a {@link StaticIpConfiguration} from the parameters in this {@link Builder}.
+ * @return The newly created StaticIpConfiguration.
+ */
+ public @NonNull StaticIpConfiguration build() {
+ final StaticIpConfiguration config = new StaticIpConfiguration();
+ config.ipAddress = mIpAddress;
+ config.gateway = mGateway;
+ if (mDnsServers != null) {
+ for (InetAddress server : mDnsServers) {
+ config.dnsServers.add(server);
+ }
+ }
+ config.domains = mDomains;
+ return config;
+ }
+ }
+
+ /**
+ * Add a DNS server to this configuration.
+ */
+ public void addDnsServer(@NonNull InetAddress server) {
+ dnsServers.add(server);
+ }
+
+ /**
+ * Returns the network routes specified by this object. Will typically include a
+ * directly-connected route for the IP address's local subnet and a default route.
+ * @param iface Interface to include in the routes.
+ */
+ public @NonNull List<RouteInfo> getRoutes(@Nullable String iface) {
+ List<RouteInfo> routes = new ArrayList<RouteInfo>(3);
+ if (ipAddress != null) {
+ RouteInfo connectedRoute = new RouteInfo(ipAddress, null, iface);
+ routes.add(connectedRoute);
+ // If the default gateway is not covered by the directly-connected route, also add a
+ // host route to the gateway as well. This configuration is arguably invalid, but it
+ // used to work in K and earlier, and other OSes appear to accept it.
+ if (gateway != null && !connectedRoute.matches(gateway)) {
+ routes.add(RouteInfo.makeHostRoute(gateway, iface));
+ }
+ }
+ if (gateway != null) {
+ routes.add(new RouteInfo((IpPrefix) null, gateway, iface));
+ }
+ return routes;
+ }
+
+ /**
+ * Returns a LinkProperties object expressing the data in this object. Note that the information
+ * contained in the LinkProperties will not be a complete picture of the link's configuration,
+ * because any configuration information that is obtained dynamically by the network (e.g.,
+ * IPv6 configuration) will not be included.
+ * @hide
+ */
+ public @NonNull LinkProperties toLinkProperties(String iface) {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ if (ipAddress != null) {
+ lp.addLinkAddress(ipAddress);
+ }
+ for (RouteInfo route : getRoutes(iface)) {
+ lp.addRoute(route);
+ }
+ for (InetAddress dns : dnsServers) {
+ lp.addDnsServer(dns);
+ }
+ lp.setDomains(domains);
+ return lp;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuffer str = new StringBuffer();
+
+ str.append("IP address ");
+ if (ipAddress != null ) str.append(ipAddress).append(" ");
+
+ str.append("Gateway ");
+ if (gateway != null) str.append(gateway.getHostAddress()).append(" ");
+
+ str.append(" DNS servers: [");
+ for (InetAddress dnsServer : dnsServers) {
+ str.append(" ").append(dnsServer.getHostAddress());
+ }
+
+ str.append(" ] Domains ");
+ if (domains != null) str.append(domains);
+ return str.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 13;
+ result = 47 * result + (ipAddress == null ? 0 : ipAddress.hashCode());
+ result = 47 * result + (gateway == null ? 0 : gateway.hashCode());
+ result = 47 * result + (domains == null ? 0 : domains.hashCode());
+ result = 47 * result + dnsServers.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof StaticIpConfiguration)) return false;
+
+ StaticIpConfiguration other = (StaticIpConfiguration) obj;
+
+ return other != null &&
+ Objects.equals(ipAddress, other.ipAddress) &&
+ Objects.equals(gateway, other.gateway) &&
+ dnsServers.equals(other.dnsServers) &&
+ Objects.equals(domains, other.domains);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @android.annotation.NonNull Creator<StaticIpConfiguration> CREATOR =
+ new Creator<StaticIpConfiguration>() {
+ public StaticIpConfiguration createFromParcel(Parcel in) {
+ return readFromParcel(in);
+ }
+
+ public StaticIpConfiguration[] newArray(int size) {
+ return new StaticIpConfiguration[size];
+ }
+ };
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(ipAddress, flags);
+ InetAddressUtils.parcelInetAddress(dest, gateway, flags);
+ dest.writeInt(dnsServers.size());
+ for (InetAddress dnsServer : dnsServers) {
+ InetAddressUtils.parcelInetAddress(dest, dnsServer, flags);
+ }
+ dest.writeString(domains);
+ }
+
+ /** @hide */
+ public static StaticIpConfiguration readFromParcel(Parcel in) {
+ final StaticIpConfiguration s = new StaticIpConfiguration();
+ s.ipAddress = in.readParcelable(null);
+ s.gateway = InetAddressUtils.unparcelInetAddress(in);
+ s.dnsServers.clear();
+ int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ s.dnsServers.add(InetAddressUtils.unparcelInetAddress(in));
+ }
+ s.domains = in.readString();
+ return s;
+ }
+}
diff --git a/framework/src/android/net/TcpKeepalivePacketData.java b/framework/src/android/net/TcpKeepalivePacketData.java
new file mode 100644
index 0000000..ddb3a6a
--- /dev/null
+++ b/framework/src/android/net/TcpKeepalivePacketData.java
@@ -0,0 +1,163 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.util.Objects;
+
+/**
+ * Represents the actual tcp keep alive packets which will be used for hardware offload.
+ * @hide
+ */
+@SystemApi
+public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+ private static final String TAG = "TcpKeepalivePacketData";
+
+ /** TCP sequence number. */
+ public final int tcpSeq;
+
+ /** TCP ACK number. */
+ public final int tcpAck;
+
+ /** TCP RCV window. */
+ public final int tcpWindow;
+
+ /** TCP RCV window scale. */
+ public final int tcpWindowScale;
+
+ /** IP TOS. */
+ public final int ipTos;
+
+ /** IP TTL. */
+ public final int ipTtl;
+
+ public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort,
+ @NonNull final InetAddress dstAddress, int dstPort, @NonNull final byte[] data,
+ int tcpSeq, int tcpAck, int tcpWindow, int tcpWindowScale, int ipTos, int ipTtl)
+ throws InvalidPacketException {
+ super(srcAddress, srcPort, dstAddress, dstPort, data);
+ this.tcpSeq = tcpSeq;
+ this.tcpAck = tcpAck;
+ this.tcpWindow = tcpWindow;
+ this.tcpWindowScale = tcpWindowScale;
+ this.ipTos = ipTos;
+ this.ipTtl = ipTtl;
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof TcpKeepalivePacketData)) return false;
+ final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
+ final InetAddress srcAddress = getSrcAddress();
+ final InetAddress dstAddress = getDstAddress();
+ return srcAddress.equals(other.getSrcAddress())
+ && dstAddress.equals(other.getDstAddress())
+ && getSrcPort() == other.getSrcPort()
+ && getDstPort() == other.getDstPort()
+ && this.tcpAck == other.tcpAck
+ && this.tcpSeq == other.tcpSeq
+ && this.tcpWindow == other.tcpWindow
+ && this.tcpWindowScale == other.tcpWindowScale
+ && this.ipTos == other.ipTos
+ && this.ipTtl == other.ipTtl;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
+ tcpAck, tcpSeq, tcpWindow, tcpWindowScale, ipTos, ipTtl);
+ }
+
+ /**
+ * Parcelable Implementation.
+ * Note that this object implements parcelable (and needs to keep doing this as it inherits
+ * from a class that does), but should usually be parceled as a stable parcelable using
+ * the toStableParcelable() and fromStableParcelable() methods.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(getSrcAddress().getHostAddress());
+ out.writeString(getDstAddress().getHostAddress());
+ out.writeInt(getSrcPort());
+ out.writeInt(getDstPort());
+ out.writeByteArray(getPacket());
+ out.writeInt(tcpSeq);
+ out.writeInt(tcpAck);
+ out.writeInt(tcpWindow);
+ out.writeInt(tcpWindowScale);
+ out.writeInt(ipTos);
+ out.writeInt(ipTtl);
+ }
+
+ private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
+ InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
+ InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
+ int srcPort = in.readInt();
+ int dstPort = in.readInt();
+ byte[] packet = in.createByteArray();
+ int tcpSeq = in.readInt();
+ int tcpAck = in.readInt();
+ int tcpWnd = in.readInt();
+ int tcpWndScale = in.readInt();
+ int ipTos = in.readInt();
+ int ipTtl = in.readInt();
+ return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
+ tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
+ }
+
+ /** Parcelable Creator. */
+ public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
+ new Parcelable.Creator<TcpKeepalivePacketData>() {
+ public TcpKeepalivePacketData createFromParcel(Parcel in) {
+ try {
+ return readFromParcel(in);
+ } catch (InvalidPacketException e) {
+ throw new IllegalArgumentException(
+ "Invalid TCP keepalive data: " + e.getError());
+ }
+ }
+
+ public TcpKeepalivePacketData[] newArray(int size) {
+ return new TcpKeepalivePacketData[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "saddr: " + getSrcAddress()
+ + " daddr: " + getDstAddress()
+ + " sport: " + getSrcPort()
+ + " dport: " + getDstPort()
+ + " seq: " + tcpSeq
+ + " ack: " + tcpAck
+ + " window: " + tcpWindow
+ + " windowScale: " + tcpWindowScale
+ + " tos: " + ipTos
+ + " ttl: " + ipTtl;
+ }
+}
diff --git a/framework/src/android/net/TcpRepairWindow.java b/framework/src/android/net/TcpRepairWindow.java
new file mode 100644
index 0000000..f062fa9
--- /dev/null
+++ b/framework/src/android/net/TcpRepairWindow.java
@@ -0,0 +1,48 @@
+/*
+ * 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.SystemApi;
+
+/**
+ * Corresponds to C's {@code struct tcp_repair_window} from
+ * include/uapi/linux/tcp.h
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TcpRepairWindow {
+ public final int sndWl1;
+ public final int sndWnd;
+ public final int maxWindow;
+ public final int rcvWnd;
+ public final int rcvWup;
+ public final int rcvWndScale;
+
+ /**
+ * Constructs an instance with the given field values.
+ */
+ public TcpRepairWindow(final int sndWl1, final int sndWnd, final int maxWindow,
+ final int rcvWnd, final int rcvWup, final int rcvWndScale) {
+ this.sndWl1 = sndWl1;
+ this.sndWnd = sndWnd;
+ this.maxWindow = maxWindow;
+ this.rcvWnd = rcvWnd;
+ this.rcvWup = rcvWup;
+ this.rcvWndScale = rcvWndScale;
+ }
+}
diff --git a/framework/src/android/net/TcpSocketKeepalive.java b/framework/src/android/net/TcpSocketKeepalive.java
new file mode 100644
index 0000000..d89814d
--- /dev/null
+++ b/framework/src/android/net/TcpSocketKeepalive.java
@@ -0,0 +1,77 @@
+/*
+ * 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.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+final class TcpSocketKeepalive extends SocketKeepalive {
+
+ TcpSocketKeepalive(@NonNull IConnectivityManager service,
+ @NonNull Network network,
+ @NonNull ParcelFileDescriptor pfd,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ super(service, network, pfd, executor, callback);
+ }
+
+ /**
+ * Starts keepalives. {@code mSocket} must be a connected TCP socket.
+ *
+ * - The application must not write to or read from the socket after calling this method, until
+ * onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
+ * with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
+ * experienced an error (as in poll(2) returned POLLERR or POLLHUP); if this happens, the data
+ * received from the socket may be invalid, and the socket can't be recovered.
+ * - If the socket has data in the send or receive buffer, then this call will fail with
+ * {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
+ * An app could ensure this by using an application-layer protocol to receive acknowledgement
+ * that indicates all data has been delivered to server, e.g. HTTP 200 OK.
+ * Then the app could go into keepalive mode after reading all remaining data within the
+ * acknowledgement.
+ */
+ @Override
+ void startImpl(int intervalSec) {
+ mExecutor.execute(() -> {
+ try {
+ mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ @Override
+ void stopImpl() {
+ mExecutor.execute(() -> {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ }
+}
diff --git a/framework/src/android/net/TestNetworkInterface.aidl b/framework/src/android/net/TestNetworkInterface.aidl
new file mode 100644
index 0000000..e1f4f9f
--- /dev/null
+++ b/framework/src/android/net/TestNetworkInterface.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+/** @hide */
+parcelable TestNetworkInterface;
diff --git a/framework/src/android/net/TestNetworkInterface.java b/framework/src/android/net/TestNetworkInterface.java
new file mode 100644
index 0000000..4449ff8
--- /dev/null
+++ b/framework/src/android/net/TestNetworkInterface.java
@@ -0,0 +1,78 @@
+/*
+ * 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.SystemApi;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+/**
+ * This class is used to return the interface name and fd of the test interface
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TestNetworkInterface implements Parcelable {
+ @NonNull
+ private final ParcelFileDescriptor mFileDescriptor;
+ @NonNull
+ private final String mInterfaceName;
+
+ @Override
+ public int describeContents() {
+ return (mFileDescriptor != null) ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(mFileDescriptor, PARCELABLE_WRITE_RETURN_VALUE);
+ out.writeString(mInterfaceName);
+ }
+
+ public TestNetworkInterface(@NonNull ParcelFileDescriptor pfd, @NonNull String intf) {
+ mFileDescriptor = pfd;
+ mInterfaceName = intf;
+ }
+
+ private TestNetworkInterface(@NonNull Parcel in) {
+ mFileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ mInterfaceName = in.readString();
+ }
+
+ @NonNull
+ public ParcelFileDescriptor getFileDescriptor() {
+ return mFileDescriptor;
+ }
+
+ @NonNull
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<TestNetworkInterface> CREATOR =
+ new Parcelable.Creator<TestNetworkInterface>() {
+ public TestNetworkInterface createFromParcel(Parcel in) {
+ return new TestNetworkInterface(in);
+ }
+
+ public TestNetworkInterface[] newArray(int size) {
+ return new TestNetworkInterface[size];
+ }
+ };
+}
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
new file mode 100644
index 0000000..4e89414
--- /dev/null
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -0,0 +1,178 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Class that allows creation and management of per-app, test-only networks
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+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;
+
+ /** @hide */
+ public TestNetworkManager(@NonNull ITestNetworkManager service) {
+ mService = Preconditions.checkNotNull(service, "missing ITestNetworkManager");
+ }
+
+ /**
+ * Teardown the capability-limited, testing-only network for a given interface
+ *
+ * @param network The test network that should be torn down
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void teardownTestNetwork(@NonNull Network network) {
+ try {
+ mService.teardownTestNetwork(network.netId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ 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
+ *
+ * @param iface the name of the interface to be used for the Network LinkProperties.
+ * @param binder A binder object guarding the lifecycle of this test network.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
+ 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);
+ }
+
+ /**
+ * Create a tun interface for testing purposes
+ *
+ * @param linkAddrs an array of LinkAddresses to assign to the TUN interface
+ * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the
+ * TUN interface.
+ * @deprecated Use {@link #createTunInterface(Collection)} instead.
+ * @hide
+ */
+ @Deprecated
+ @NonNull
+ public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) {
+ return createTunInterface(Arrays.asList(linkAddrs));
+ }
+
+ /**
+ * Create a tun interface for testing purposes
+ *
+ * @param linkAddrs an array of LinkAddresses to assign to the TUN interface
+ * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the
+ * TUN interface.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @NonNull
+ public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
+ try {
+ final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
+ return mService.createTunInterface(linkAddrs.toArray(arr));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Create a tap interface for testing purposes
+ *
+ * @return A ParcelFileDescriptor of the underlying TAP interface. Close this to tear down the
+ * TAP interface.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @NonNull
+ public TestNetworkInterface createTapInterface() {
+ try {
+ return mService.createTapInterface();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/framework/src/android/net/TransportInfo.java b/framework/src/android/net/TransportInfo.java
new file mode 100644
index 0000000..aa4bbb0
--- /dev/null
+++ b/framework/src/android/net/TransportInfo.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * A container for transport-specific capabilities which is returned by
+ * {@link NetworkCapabilities#getTransportInfo()}. Specific networks
+ * may provide concrete implementations of this interface.
+ * @see android.net.wifi.aware.WifiAwareNetworkInfo
+ * @see android.net.wifi.WifiInfo
+ */
+public interface TransportInfo {
+
+ /**
+ * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that
+ * were set based on the permissions of the process that originally received it.
+ *
+ * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as
+ * they should not be shared outside of the process that receives them without appropriate
+ * checks.
+ *
+ * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept
+ * when parceling
+ * @return Copy of this instance.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+ return this;
+ }
+
+ /**
+ * Returns whether this TransportInfo type has location sensitive fields or not (helps
+ * to determine whether to perform a location permission check or not before sending to
+ * apps).
+ *
+ * @return {@code true} if this instance contains location sensitive info, {@code false}
+ * otherwise.
+ * @hide
+ */
+ @SystemApi
+ default boolean hasLocationSensitiveFields() {
+ return false;
+ }
+}
diff --git a/framework/src/android/net/UidRange.aidl b/framework/src/android/net/UidRange.aidl
new file mode 100644
index 0000000..f70fc8e
--- /dev/null
+++ b/framework/src/android/net/UidRange.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * An inclusive range of UIDs.
+ *
+ * {@hide}
+ */
+parcelable UidRange;
\ No newline at end of file
diff --git a/framework/src/android/net/VpnManager.java b/framework/src/android/net/VpnManager.java
new file mode 100644
index 0000000..c87b827
--- /dev/null
+++ b/framework/src/android/net/VpnManager.java
@@ -0,0 +1,164 @@
+/*
+ * 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 com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
+
+/**
+ * This class provides an interface for apps to manage platform VPN profiles
+ *
+ * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
+ * further app intermediation. When a VPN profile is present and the app is selected as an always-on
+ * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
+ * app (unlike VpnService).
+ *
+ * <p>VPN apps using supported protocols should preferentially use this API over the {@link
+ * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
+ * the guarantee that VPN network traffic is not subjected to on-device packet interception.
+ *
+ * @see Ikev2VpnProfile
+ */
+public class VpnManager {
+ /** Type representing a lack of VPN @hide */
+ public static final int TYPE_VPN_NONE = -1;
+ /** VPN service type code @hide */
+ public static final int TYPE_VPN_SERVICE = 1;
+ /** Platform VPN type code @hide */
+ public static final int TYPE_VPN_PLATFORM = 2;
+
+ /** @hide */
+ @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VpnType {}
+
+ @NonNull private final Context mContext;
+ @NonNull private final IConnectivityManager mService;
+
+ private static Intent getIntentForConfirmation() {
+ final Intent intent = new Intent();
+ final ComponentName componentName = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
+ intent.setComponent(componentName);
+ return intent;
+ }
+
+ /**
+ * Create an instance of the VpnManager with the given context.
+ *
+ * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
+ * {@link Context.getSystemService()} method call.
+ *
+ * @hide
+ */
+ public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
+ mContext = checkNotNull(ctx, "missing Context");
+ mService = checkNotNull(service, "missing IConnectivityManager");
+ }
+
+ /**
+ * Install a VpnProfile configuration keyed on the calling app's package name.
+ *
+ * <p>This method returns {@code null} if user consent has already been granted, or an {@link
+ * Intent} to a system activity. If an intent is returned, the application should launch the
+ * activity using {@link Activity#startActivityForResult} to request user consent. The activity
+ * may pop up a dialog to require user action, and the result will come back via its {@link
+ * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
+ * consented, and the VPN profile can be started.
+ *
+ * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
+ * stored for this package.
+ * @return an Intent requesting user consent to start the VPN, or null if consent is not
+ * required based on privileges or previous user consent.
+ */
+ @Nullable
+ public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
+ final VpnProfile internalProfile;
+
+ try {
+ internalProfile = profile.toVpnProfile();
+ } catch (GeneralSecurityException | IOException e) {
+ // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
+ // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
+ // string as required by the VpnProfile.
+ throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
+ }
+
+ try {
+ // Profile can never be null; it either gets set, or an exception is thrown.
+ if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return getIntentForConfirmation();
+ }
+
+ /**
+ * Delete the VPN profile configuration that was provisioned by the calling app
+ *
+ * @throws SecurityException if this would violate user settings
+ */
+ public void deleteProvisionedVpnProfile() {
+ try {
+ mService.deleteVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request the startup of a previously provisioned VPN.
+ *
+ * @throws SecurityException exception if user or device settings prevent this VPN from being
+ * setup, or if user consent has not been granted
+ */
+ public void startProvisionedVpnProfile() {
+ try {
+ mService.startVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Tear down the VPN provided by the calling app (if any) */
+ public void stopProvisionedVpnProfile() {
+ try {
+ mService.stopVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/framework/src/android/net/VpnService.java b/framework/src/android/net/VpnService.java
new file mode 100644
index 0000000..8e90a11
--- /dev/null
+++ b/framework/src/android/net/VpnService.java
@@ -0,0 +1,903 @@
+/*
+ * Copyright (C) 2011 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.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+
+import com.android.internal.net.VpnConfig;
+
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * VpnService is a base class for applications to extend and build their
+ * own VPN solutions. In general, it creates a virtual network interface,
+ * configures addresses and routing rules, and returns a file descriptor
+ * to the application. Each read from the descriptor retrieves an outgoing
+ * packet which was routed to the interface. Each write to the descriptor
+ * injects an incoming packet just like it was received from the interface.
+ * The interface is running on Internet Protocol (IP), so packets are
+ * always started with IP headers. The application then completes a VPN
+ * connection by processing and exchanging packets with the remote server
+ * over a tunnel.
+ *
+ * <p>Letting applications intercept packets raises huge security concerns.
+ * A VPN application can easily break the network. Besides, two of them may
+ * conflict with each other. The system takes several actions to address
+ * these issues. Here are some key points:
+ * <ul>
+ * <li>User action is required the first time an application creates a VPN
+ * connection.</li>
+ * <li>There can be only one VPN connection running at the same time. The
+ * existing interface is deactivated when a new one is created.</li>
+ * <li>A system-managed notification is shown during the lifetime of a
+ * VPN connection.</li>
+ * <li>A system-managed dialog gives the information of the current VPN
+ * connection. It also provides a button to disconnect.</li>
+ * <li>The network is restored automatically when the file descriptor is
+ * closed. It also covers the cases when a VPN application is crashed
+ * or killed by the system.</li>
+ * </ul>
+ *
+ * <p>There are two primary methods in this class: {@link #prepare} and
+ * {@link Builder#establish}. The former deals with user action and stops
+ * the VPN connection created by another application. The latter creates
+ * a VPN interface using the parameters supplied to the {@link Builder}.
+ * An application must call {@link #prepare} to grant the right to use
+ * other methods in this class, and the right can be revoked at any time.
+ * Here are the general steps to create a VPN connection:
+ * <ol>
+ * <li>When the user presses the button to connect, call {@link #prepare}
+ * and launch the returned intent, if non-null.</li>
+ * <li>When the application becomes prepared, start the service.</li>
+ * <li>Create a tunnel to the remote server and negotiate the network
+ * parameters for the VPN connection.</li>
+ * <li>Supply those parameters to a {@link Builder} and create a VPN
+ * interface by calling {@link Builder#establish}.</li>
+ * <li>Process and exchange packets between the tunnel and the returned
+ * file descriptor.</li>
+ * <li>When {@link #onRevoke} is invoked, close the file descriptor and
+ * shut down the tunnel gracefully.</li>
+ * </ol>
+ *
+ * <p>Services extending this class need to be declared with an appropriate
+ * permission and intent filter. Their access must be secured by
+ * {@link android.Manifest.permission#BIND_VPN_SERVICE} permission, and
+ * their intent filter must match {@link #SERVICE_INTERFACE} action. Here
+ * is an example of declaring a VPN service in {@code AndroidManifest.xml}:
+ * <pre>
+ * <service android:name=".ExampleVpnService"
+ * android:permission="android.permission.BIND_VPN_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.net.VpnService"/>
+ * </intent-filter>
+ * </service></pre>
+ *
+ * <p> The Android system starts a VPN in the background by calling
+ * {@link android.content.Context#startService startService()}. In Android 8.0
+ * (API level 26) and higher, the system places VPN apps on the temporary
+ * allowlist for a short period so the app can start in the background. The VPN
+ * app must promote itself to the foreground after it's launched or the system
+ * will shut down the app.
+ *
+ * <h3>Developer's guide</h3>
+ *
+ * <p>To learn more about developing VPN apps, read the
+ * <a href="{@docRoot}guide/topics/connectivity/vpn">VPN developer's guide</a>.
+ *
+ * @see Builder
+ */
+public class VpnService extends Service {
+
+ /**
+ * The action must be matched by the intent filter of this service. It also
+ * needs to require {@link android.Manifest.permission#BIND_VPN_SERVICE}
+ * permission so that other applications cannot abuse it.
+ */
+ public static final String SERVICE_INTERFACE = VpnConfig.SERVICE_INTERFACE;
+
+ /**
+ * Key for boolean meta-data field indicating whether this VpnService supports always-on mode.
+ *
+ * <p>For a VPN app targeting {@link android.os.Build.VERSION_CODES#N API 24} or above, Android
+ * provides users with the ability to set it as always-on, so that VPN connection is
+ * persisted after device reboot and app upgrade. Always-on VPN can also be enabled by device
+ * owner and profile owner apps through
+ * {@link DevicePolicyManager#setAlwaysOnVpnPackage}.
+ *
+ * <p>VPN apps not supporting this feature should opt out by adding this meta-data field to the
+ * {@code VpnService} component of {@code AndroidManifest.xml}. In case there is more than one
+ * {@code VpnService} component defined in {@code AndroidManifest.xml}, opting out any one of
+ * them will opt out the entire app. For example,
+ * <pre> {@code
+ * <service android:name=".ExampleVpnService"
+ * android:permission="android.permission.BIND_VPN_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.net.VpnService"/>
+ * </intent-filter>
+ * <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
+ * android:value=false/>
+ * </service>
+ * } </pre>
+ *
+ * <p>This meta-data field defaults to {@code true} if absent. It will only have effect on
+ * {@link android.os.Build.VERSION_CODES#O_MR1} or higher.
+ */
+ public static final String SERVICE_META_DATA_SUPPORTS_ALWAYS_ON =
+ "android.net.VpnService.SUPPORTS_ALWAYS_ON";
+
+ /**
+ * Use IConnectivityManager since those methods are hidden and not
+ * available in ConnectivityManager.
+ */
+ private static IConnectivityManager getService() {
+ return IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ }
+
+ /**
+ * Prepare to establish a VPN connection. This method returns {@code null}
+ * if the VPN application is already prepared or if the user has previously
+ * consented to the VPN application. Otherwise, it returns an
+ * {@link Intent} to a system activity. The application should launch the
+ * activity using {@link Activity#startActivityForResult} to get itself
+ * prepared. The activity may pop up a dialog to require user action, and
+ * the result will come back via its {@link Activity#onActivityResult}.
+ * If the result is {@link Activity#RESULT_OK}, the application becomes
+ * prepared and is granted to use other methods in this class.
+ *
+ * <p>Only one application can be granted at the same time. The right
+ * is revoked when another application is granted. The application
+ * losing the right will be notified via its {@link #onRevoke}. Unless
+ * it becomes prepared again, subsequent calls to other methods in this
+ * class will fail.
+ *
+ * <p>The user may disable the VPN at any time while it is activated, in
+ * which case this method will return an intent the next time it is
+ * executed to obtain the user's consent again.
+ *
+ * @see #onRevoke
+ */
+ public static Intent prepare(Context context) {
+ try {
+ if (getService().prepareVpn(context.getPackageName(), null, context.getUserId())) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return VpnConfig.getIntentForConfirmation();
+ }
+
+ /**
+ * Version of {@link #prepare(Context)} which does not require user consent.
+ *
+ * <p>Requires {@link android.Manifest.permission#CONTROL_VPN} and should generally not be
+ * used. Only acceptable in situations where user consent has been obtained through other means.
+ *
+ * <p>Once this is run, future preparations may be done with the standard prepare method as this
+ * will authorize the package to prepare the VPN without consent in the future.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CONTROL_VPN)
+ public static void prepareAndAuthorize(Context context) {
+ IConnectivityManager cm = getService();
+ String packageName = context.getPackageName();
+ try {
+ // Only prepare if we're not already prepared.
+ int userId = context.getUserId();
+ if (!cm.prepareVpn(packageName, null, userId)) {
+ cm.prepareVpn(null, packageName, userId);
+ }
+ cm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+
+ /**
+ * Protect a socket from VPN connections. After protecting, data sent
+ * through this socket will go directly to the underlying network,
+ * so its traffic will not be forwarded through the VPN.
+ * This method is useful if some connections need to be kept
+ * outside of VPN. For example, a VPN tunnel should protect itself if its
+ * destination is covered by VPN routes. Otherwise its outgoing packets
+ * will be sent back to the VPN interface and cause an infinite loop. This
+ * method will fail if the application is not prepared or is revoked.
+ *
+ * <p class="note">The socket is NOT closed by this method.
+ *
+ * @return {@code true} on success.
+ */
+ public boolean protect(int socket) {
+ return NetworkUtils.protectFromVpn(socket);
+ }
+
+ /**
+ * Convenience method to protect a {@link Socket} from VPN connections.
+ *
+ * @return {@code true} on success.
+ * @see #protect(int)
+ */
+ public boolean protect(Socket socket) {
+ return protect(socket.getFileDescriptor$().getInt$());
+ }
+
+ /**
+ * Convenience method to protect a {@link DatagramSocket} from VPN
+ * connections.
+ *
+ * @return {@code true} on success.
+ * @see #protect(int)
+ */
+ public boolean protect(DatagramSocket socket) {
+ return protect(socket.getFileDescriptor$().getInt$());
+ }
+
+ /**
+ * Adds a network address to the VPN interface.
+ *
+ * Both IPv4 and IPv6 addresses are supported. The VPN must already be established. Fails if the
+ * address is already in use or cannot be assigned to the interface for any other reason.
+ *
+ * Adding an address implicitly allows traffic from that address family (i.e., IPv4 or IPv6) to
+ * be routed over the VPN. @see Builder#allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ *
+ * @param address The IP address (IPv4 or IPv6) to assign to the VPN interface.
+ * @param prefixLength The prefix length of the address.
+ *
+ * @return {@code true} on success.
+ * @see Builder#addAddress
+ *
+ * @hide
+ */
+ public boolean addAddress(InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+ try {
+ return getService().addVpnAddress(address.getHostAddress(), prefixLength);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Removes a network address from the VPN interface.
+ *
+ * Both IPv4 and IPv6 addresses are supported. The VPN must already be established. Fails if the
+ * address is not assigned to the VPN interface, or if it is the only address assigned (thus
+ * cannot be removed), or if the address cannot be removed for any other reason.
+ *
+ * After removing an address, if there are no addresses, routes or DNS servers of a particular
+ * address family (i.e., IPv4 or IPv6) configured on the VPN, that <b>DOES NOT</b> block that
+ * family from being routed. In other words, once an address family has been allowed, it stays
+ * allowed for the rest of the VPN's session. @see Builder#allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ *
+ * @param address The IP address (IPv4 or IPv6) to assign to the VPN interface.
+ * @param prefixLength The prefix length of the address.
+ *
+ * @return {@code true} on success.
+ *
+ * @hide
+ */
+ public boolean removeAddress(InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+ try {
+ return getService().removeVpnAddress(address.getHostAddress(), prefixLength);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Sets the underlying networks used by the VPN for its upstream connections.
+ *
+ * <p>Used by the system to know the actual networks that carry traffic for apps affected by
+ * this VPN in order to present this information to the user (e.g., via status bar icons).
+ *
+ * <p>This method only needs to be called if the VPN has explicitly bound its underlying
+ * communications channels — such as the socket(s) passed to {@link #protect(int)} —
+ * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or
+ * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time
+ * the set of {@code Network}s it is using changes.
+ *
+ * <p>{@code networks} is one of the following:
+ * <ul>
+ * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
+ * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
+ * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
+ * first in the array.</li>
+ * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
+ * underlying network connection, and thus, app traffic will not be sent or received.</li>
+ * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
+ * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
+ * APIs mentioned above to send traffic over specific channels.</li>
+ * </ul>
+ *
+ * <p>This call will succeed only if the VPN is currently established. For setting this value
+ * when the VPN has not yet been established, see {@link Builder#setUnderlyingNetworks}.
+ *
+ * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
+ *
+ * @return {@code true} on success.
+ */
+ public boolean setUnderlyingNetworks(Network[] networks) {
+ try {
+ return getService().setUnderlyingNetworksForVpn(networks);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Returns whether the service is running in always-on VPN mode. In this mode the system ensures
+ * that the service is always running by restarting it when necessary, e.g. after reboot.
+ *
+ * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
+ */
+ public final boolean isAlwaysOn() {
+ try {
+ return getService().isCallerCurrentAlwaysOnVpnApp();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the service is running in always-on VPN lockdown mode. In this mode the
+ * system ensures that the service is always running and that the apps aren't allowed to bypass
+ * the VPN.
+ *
+ * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)
+ */
+ public final boolean isLockdownEnabled() {
+ try {
+ return getService().isCallerCurrentAlwaysOnVpnLockdownApp();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the communication interface to the service. This method returns
+ * {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE}
+ * action. Applications overriding this method must identify the intent
+ * and return the corresponding interface accordingly.
+ *
+ * @see Service#onBind
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (intent != null && SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new Callback();
+ }
+ return null;
+ }
+
+ /**
+ * Invoked when the application is revoked. At this moment, the VPN
+ * interface is already deactivated by the system. The application should
+ * close the file descriptor and shut down gracefully. The default
+ * implementation of this method is calling {@link Service#stopSelf()}.
+ *
+ * <p class="note">Calls to this method may not happen on the main thread
+ * of the process.
+ *
+ * @see #prepare
+ */
+ public void onRevoke() {
+ stopSelf();
+ }
+
+ /**
+ * Use raw Binder instead of AIDL since now there is only one usage.
+ */
+ private class Callback extends Binder {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
+ if (code == IBinder.LAST_CALL_TRANSACTION) {
+ onRevoke();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Private method to validate address and prefixLength.
+ */
+ private static void check(InetAddress address, int prefixLength) {
+ if (address.isLoopbackAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ if (address instanceof Inet4Address) {
+ if (prefixLength < 0 || prefixLength > 32) {
+ throw new IllegalArgumentException("Bad prefixLength");
+ }
+ } else if (address instanceof Inet6Address) {
+ if (prefixLength < 0 || prefixLength > 128) {
+ throw new IllegalArgumentException("Bad prefixLength");
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported family");
+ }
+ }
+
+ /**
+ * Helper class to create a VPN interface. This class should be always
+ * used within the scope of the outer {@link VpnService}.
+ *
+ * @see VpnService
+ */
+ public class Builder {
+
+ private final VpnConfig mConfig = new VpnConfig();
+ @UnsupportedAppUsage
+ private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+ @UnsupportedAppUsage
+ private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+
+ public Builder() {
+ mConfig.user = VpnService.this.getClass().getName();
+ }
+
+ /**
+ * Set the name of this session. It will be displayed in
+ * system-managed dialogs and notifications. This is recommended
+ * not required.
+ */
+ @NonNull
+ public Builder setSession(@NonNull String session) {
+ mConfig.session = session;
+ return this;
+ }
+
+ /**
+ * Set the {@link PendingIntent} to an activity for users to
+ * configure the VPN connection. If it is not set, the button
+ * to configure will not be shown in system-managed dialogs.
+ */
+ @NonNull
+ public Builder setConfigureIntent(@NonNull PendingIntent intent) {
+ mConfig.configureIntent = intent;
+ return this;
+ }
+
+ /**
+ * Set the maximum transmission unit (MTU) of the VPN interface. If
+ * it is not set, the default value in the operating system will be
+ * used.
+ *
+ * @throws IllegalArgumentException if the value is not positive.
+ */
+ @NonNull
+ public Builder setMtu(int mtu) {
+ if (mtu <= 0) {
+ throw new IllegalArgumentException("Bad mtu");
+ }
+ mConfig.mtu = mtu;
+ return this;
+ }
+
+ /**
+ * Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
+ * and it is possible that some apps will ignore it.
+ */
+ @NonNull
+ public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) {
+ mConfig.proxyInfo = proxyInfo;
+ return this;
+ }
+
+ /**
+ * Add a network address to the VPN interface. Both IPv4 and IPv6
+ * addresses are supported. At least one address must be set before
+ * calling {@link #establish}.
+ *
+ * Adding an address implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ */
+ @NonNull
+ public Builder addAddress(@NonNull InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+
+ if (address.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ mAddresses.add(new LinkAddress(address, prefixLength));
+ mConfig.updateAllowedFamilies(address);
+ return this;
+ }
+
+ /**
+ * Convenience method to add a network address to the VPN interface
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * Adding an address implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ * @see #addAddress(InetAddress, int)
+ */
+ @NonNull
+ public Builder addAddress(@NonNull String address, int prefixLength) {
+ return addAddress(InetAddress.parseNumericAddress(address), prefixLength);
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Adding a route implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
+ check(address, prefixLength);
+
+ int offset = prefixLength / 8;
+ byte[] bytes = address.getAddress();
+ if (offset < bytes.length) {
+ for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
+ if (bytes[offset] != 0) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ }
+ }
+ mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null));
+ mConfig.updateAllowedFamilies(address);
+ return this;
+ }
+
+ /**
+ * Convenience method to add a network route to the VPN interface
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * Adding a route implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ * @see #addRoute(InetAddress, int)
+ */
+ @NonNull
+ public Builder addRoute(@NonNull String address, int prefixLength) {
+ return addRoute(InetAddress.parseNumericAddress(address), prefixLength);
+ }
+
+ /**
+ * Add a DNS server to the VPN connection. Both IPv4 and IPv6
+ * addresses are supported. If none is set, the DNS servers of
+ * the default network will be used.
+ *
+ * Adding a server implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ */
+ @NonNull
+ public Builder addDnsServer(@NonNull InetAddress address) {
+ if (address.isLoopbackAddress() || address.isAnyLocalAddress()) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ if (mConfig.dnsServers == null) {
+ mConfig.dnsServers = new ArrayList<String>();
+ }
+ mConfig.dnsServers.add(address.getHostAddress());
+ return this;
+ }
+
+ /**
+ * Convenience method to add a DNS server to the VPN connection
+ * using a numeric address string. See {@link InetAddress} for the
+ * definitions of numeric address formats.
+ *
+ * Adding a server implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * @throws IllegalArgumentException if the address is invalid.
+ * @see #addDnsServer(InetAddress)
+ */
+ @NonNull
+ public Builder addDnsServer(@NonNull String address) {
+ return addDnsServer(InetAddress.parseNumericAddress(address));
+ }
+
+ /**
+ * Add a search domain to the DNS resolver.
+ */
+ @NonNull
+ public Builder addSearchDomain(@NonNull String domain) {
+ if (mConfig.searchDomains == null) {
+ mConfig.searchDomains = new ArrayList<String>();
+ }
+ mConfig.searchDomains.add(domain);
+ return this;
+ }
+
+ /**
+ * Allows traffic from the specified address family.
+ *
+ * By default, if no address, route or DNS server of a specific family (IPv4 or IPv6) is
+ * added to this VPN, then all outgoing traffic of that family is blocked. If any address,
+ * route or DNS server is added, that family is allowed.
+ *
+ * This method allows an address family to be unblocked even without adding an address,
+ * route or DNS server of that family. Traffic of that family will then typically
+ * fall-through to the underlying network if it's supported.
+ *
+ * {@code family} must be either {@code AF_INET} (for IPv4) or {@code AF_INET6} (for IPv6).
+ * {@link IllegalArgumentException} is thrown if it's neither.
+ *
+ * @param family The address family ({@code AF_INET} or {@code AF_INET6}) to allow.
+ *
+ * @return this {@link Builder} object to facilitate chaining of method calls.
+ */
+ @NonNull
+ public Builder allowFamily(int family) {
+ if (family == AF_INET) {
+ mConfig.allowIPv4 = true;
+ } else if (family == AF_INET6) {
+ mConfig.allowIPv6 = true;
+ } else {
+ throw new IllegalArgumentException(family + " is neither " + AF_INET + " nor " +
+ AF_INET6);
+ }
+ return this;
+ }
+
+ private void verifyApp(String packageName) throws PackageManager.NameNotFoundException {
+ IPackageManager pm = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ try {
+ pm.getApplicationInfo(packageName, 0, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Adds an application that's allowed to access the VPN connection.
+ *
+ * If this method is called at least once, only applications added through this method (and
+ * no others) are allowed access. Else (if this method is never called), all applications
+ * are allowed by default. If some applications are added, other, un-added applications
+ * will use networking as if the VPN wasn't running.
+ *
+ * A {@link Builder} may have only a set of allowed applications OR a set of disallowed
+ * ones, but not both. Calling this method after {@link #addDisallowedApplication} has
+ * already been called, or vice versa, will throw an {@link UnsupportedOperationException}.
+ *
+ * {@code packageName} must be the canonical name of a currently installed application.
+ * {@link PackageManager.NameNotFoundException} is thrown if there's no such application.
+ *
+ * @throws PackageManager.NameNotFoundException If the application isn't installed.
+ *
+ * @param packageName The full name (e.g.: "com.google.apps.contacts") of an application.
+ *
+ * @return this {@link Builder} object to facilitate chaining method calls.
+ */
+ @NonNull
+ public Builder addAllowedApplication(@NonNull String packageName)
+ throws PackageManager.NameNotFoundException {
+ if (mConfig.disallowedApplications != null) {
+ throw new UnsupportedOperationException("addDisallowedApplication already called");
+ }
+ verifyApp(packageName);
+ if (mConfig.allowedApplications == null) {
+ mConfig.allowedApplications = new ArrayList<String>();
+ }
+ mConfig.allowedApplications.add(packageName);
+ return this;
+ }
+
+ /**
+ * Adds an application that's denied access to the VPN connection.
+ *
+ * By default, all applications are allowed access, except for those denied through this
+ * method. Denied applications will use networking as if the VPN wasn't running.
+ *
+ * A {@link Builder} may have only a set of allowed applications OR a set of disallowed
+ * ones, but not both. Calling this method after {@link #addAllowedApplication} has already
+ * been called, or vice versa, will throw an {@link UnsupportedOperationException}.
+ *
+ * {@code packageName} must be the canonical name of a currently installed application.
+ * {@link PackageManager.NameNotFoundException} is thrown if there's no such application.
+ *
+ * @throws PackageManager.NameNotFoundException If the application isn't installed.
+ *
+ * @param packageName The full name (e.g.: "com.google.apps.contacts") of an application.
+ *
+ * @return this {@link Builder} object to facilitate chaining method calls.
+ */
+ @NonNull
+ public Builder addDisallowedApplication(@NonNull String packageName)
+ throws PackageManager.NameNotFoundException {
+ if (mConfig.allowedApplications != null) {
+ throw new UnsupportedOperationException("addAllowedApplication already called");
+ }
+ verifyApp(packageName);
+ if (mConfig.disallowedApplications == null) {
+ mConfig.disallowedApplications = new ArrayList<String>();
+ }
+ mConfig.disallowedApplications.add(packageName);
+ return this;
+ }
+
+ /**
+ * Allows all apps to bypass this VPN connection.
+ *
+ * By default, all traffic from apps is forwarded through the VPN interface and it is not
+ * possible for apps to side-step the VPN. If this method is called, apps may use methods
+ * such as {@link ConnectivityManager#bindProcessToNetwork} to instead send/receive
+ * directly over the underlying network or any other network they have permissions for.
+ *
+ * @return this {@link Builder} object to facilitate chaining of method calls.
+ */
+ @NonNull
+ public Builder allowBypass() {
+ mConfig.allowBypass = true;
+ return this;
+ }
+
+ /**
+ * Sets the VPN interface's file descriptor to be in blocking/non-blocking mode.
+ *
+ * By default, the file descriptor returned by {@link #establish} is non-blocking.
+ *
+ * @param blocking True to put the descriptor into blocking mode; false for non-blocking.
+ *
+ * @return this {@link Builder} object to facilitate chaining method calls.
+ */
+ @NonNull
+ public Builder setBlocking(boolean blocking) {
+ mConfig.blocking = blocking;
+ return this;
+ }
+
+ /**
+ * Sets the underlying networks used by the VPN for its upstream connections.
+ *
+ * @see VpnService#setUnderlyingNetworks
+ *
+ * @param networks An array of networks the VPN uses to tunnel traffic to/from its servers.
+ *
+ * @return this {@link Builder} object to facilitate chaining method calls.
+ */
+ @NonNull
+ public Builder setUnderlyingNetworks(@Nullable Network[] networks) {
+ mConfig.underlyingNetworks = networks != null ? networks.clone() : null;
+ return this;
+ }
+
+ /**
+ * Marks the VPN network as metered. A VPN network is classified as metered when the user is
+ * sensitive to heavy data usage due to monetary costs and/or data limitations. In such
+ * cases, you should set this to {@code true} so that apps on the system can avoid doing
+ * large data transfers. Otherwise, set this to {@code false}. Doing so would cause VPN
+ * network to inherit its meteredness from its underlying networks.
+ *
+ * <p>VPN apps targeting {@link android.os.Build.VERSION_CODES#Q} or above will be
+ * considered metered by default.
+ *
+ * @param isMetered {@code true} if VPN network should be treated as metered regardless of
+ * underlying network meteredness
+ * @return this {@link Builder} object to facilitate chaining method calls
+ * @see #setUnderlyingNetworks(Network[])
+ * @see ConnectivityManager#isActiveNetworkMetered()
+ */
+ @NonNull
+ public Builder setMetered(boolean isMetered) {
+ mConfig.isMetered = isMetered;
+ return this;
+ }
+
+ /**
+ * Create a VPN interface using the parameters supplied to this
+ * builder. The interface works on IP packets, and a file descriptor
+ * is returned for the application to access them. Each read
+ * retrieves an outgoing packet which was routed to the interface.
+ * Each write injects an incoming packet just like it was received
+ * from the interface. The file descriptor is put into non-blocking
+ * mode by default to avoid blocking Java threads. To use the file
+ * descriptor completely in native space, see
+ * {@link ParcelFileDescriptor#detachFd()}. The application MUST
+ * close the file descriptor when the VPN connection is terminated.
+ * The VPN interface will be removed and the network will be
+ * restored by the system automatically.
+ *
+ * <p>To avoid conflicts, there can be only one active VPN interface
+ * at the same time. Usually network parameters are never changed
+ * during the lifetime of a VPN connection. It is also common for an
+ * application to create a new file descriptor after closing the
+ * previous one. However, it is rare but not impossible to have two
+ * interfaces while performing a seamless handover. In this case, the
+ * old interface will be deactivated when the new one is created
+ * successfully. Both file descriptors are valid but now outgoing
+ * packets will be routed to the new interface. Therefore, after
+ * draining the old file descriptor, the application MUST close it
+ * and start using the new file descriptor. If the new interface
+ * cannot be created, the existing interface and its file descriptor
+ * remain untouched.
+ *
+ * <p>An exception will be thrown if the interface cannot be created
+ * for any reason. However, this method returns {@code null} if the
+ * application is not prepared or is revoked. This helps solve
+ * possible race conditions between other VPN applications.
+ *
+ * @return {@link ParcelFileDescriptor} of the VPN interface, or
+ * {@code null} if the application is not prepared.
+ * @throws IllegalArgumentException if a parameter is not accepted
+ * by the operating system.
+ * @throws IllegalStateException if a parameter cannot be applied
+ * by the operating system.
+ * @throws SecurityException if the service is not properly declared
+ * in {@code AndroidManifest.xml}.
+ * @see VpnService
+ */
+ @Nullable
+ public ParcelFileDescriptor establish() {
+ mConfig.addresses = mAddresses;
+ mConfig.routes = mRoutes;
+
+ try {
+ return getService().establishVpn(mConfig);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+}
diff --git a/framework/src/android/net/apf/ApfCapabilities.aidl b/framework/src/android/net/apf/ApfCapabilities.aidl
new file mode 100644
index 0000000..7c4d4c2
--- /dev/null
+++ b/framework/src/android/net/apf/ApfCapabilities.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.apf;
+
+@JavaOnlyStableParcelable parcelable ApfCapabilities;
\ No newline at end of file
diff --git a/framework/src/android/net/apf/ApfCapabilities.java b/framework/src/android/net/apf/ApfCapabilities.java
new file mode 100644
index 0000000..bf5b26e
--- /dev/null
+++ b/framework/src/android/net/apf/ApfCapabilities.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 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.apf;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.res.Resources;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.R;
+
+/**
+ * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
+ * way to drop unwanted network packets to save power.
+ *
+ * See documentation at hardware/google/apf/apf.h
+ *
+ * This class is immutable.
+ * @hide
+ */
+@SystemApi
+public final class ApfCapabilities implements Parcelable {
+ /**
+ * Version of APF instruction set supported for packet filtering. 0 indicates no support for
+ * packet filtering using APF programs.
+ */
+ public final int apfVersionSupported;
+
+ /**
+ * Maximum size of APF program allowed.
+ */
+ public final int maximumApfProgramSize;
+
+ /**
+ * Format of packets passed to APF filter. Should be one of ARPHRD_*
+ */
+ public final int apfPacketFormat;
+
+ public ApfCapabilities(
+ int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
+ this.apfVersionSupported = apfVersionSupported;
+ this.maximumApfProgramSize = maximumApfProgramSize;
+ this.apfPacketFormat = apfPacketFormat;
+ }
+
+ private ApfCapabilities(Parcel in) {
+ apfVersionSupported = in.readInt();
+ maximumApfProgramSize = in.readInt();
+ apfPacketFormat = in.readInt();
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(apfVersionSupported);
+ dest.writeInt(maximumApfProgramSize);
+ dest.writeInt(apfPacketFormat);
+ }
+
+ public static final Creator<ApfCapabilities> CREATOR = new Creator<ApfCapabilities>() {
+ @Override
+ public ApfCapabilities createFromParcel(Parcel in) {
+ return new ApfCapabilities(in);
+ }
+
+ @Override
+ public ApfCapabilities[] newArray(int size) {
+ return new ApfCapabilities[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
+ apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof ApfCapabilities)) return false;
+ final ApfCapabilities other = (ApfCapabilities) obj;
+ return apfVersionSupported == other.apfVersionSupported
+ && maximumApfProgramSize == other.maximumApfProgramSize
+ && apfPacketFormat == other.apfPacketFormat;
+ }
+
+ /**
+ * Determines whether the APF interpreter advertises support for the data buffer access opcodes
+ * LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and
+ * STDW (STore Data Word) support is present from APFv4 on.
+ *
+ * @return {@code true} if the IWifiStaIface#readApfPacketFilterData is supported.
+ */
+ public boolean hasDataAccess() {
+ return apfVersionSupported >= 4;
+ }
+
+ /**
+ * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
+ */
+ public static boolean getApfDrop8023Frames() {
+ return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames);
+ }
+
+ /**
+ * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
+ */
+ public static @NonNull int[] getApfEtherTypeBlackList() {
+ return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
+ }
+}
diff --git a/framework/src/android/net/util/DnsUtils.java b/framework/src/android/net/util/DnsUtils.java
new file mode 100644
index 0000000..7908353
--- /dev/null
+++ b/framework/src/android/net/util/DnsUtils.java
@@ -0,0 +1,379 @@
+/*
+ * 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.util;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.InetAddresses;
+import android.net.Network;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.util.BitUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class DnsUtils {
+ private static final String TAG = "DnsUtils";
+ private static final int CHAR_BIT = 8;
+ public static final int IPV6_ADDR_SCOPE_NODELOCAL = 0x01;
+ public static final int IPV6_ADDR_SCOPE_LINKLOCAL = 0x02;
+ public static final int IPV6_ADDR_SCOPE_SITELOCAL = 0x05;
+ public static final int IPV6_ADDR_SCOPE_GLOBAL = 0x0e;
+ private static final Comparator<SortableAddress> sRfc6724Comparator = new Rfc6724Comparator();
+
+ /**
+ * Comparator to sort SortableAddress in Rfc6724 style.
+ */
+ public static class Rfc6724Comparator implements Comparator<SortableAddress> {
+ // This function matches the behaviour of _rfc6724_compare in the native resolver.
+ @Override
+ public int compare(SortableAddress span1, SortableAddress span2) {
+ // Rule 1: Avoid unusable destinations.
+ if (span1.hasSrcAddr != span2.hasSrcAddr) {
+ return span2.hasSrcAddr - span1.hasSrcAddr;
+ }
+
+ // Rule 2: Prefer matching scope.
+ if (span1.scopeMatch != span2.scopeMatch) {
+ return span2.scopeMatch - span1.scopeMatch;
+ }
+
+ // TODO: Implement rule 3: Avoid deprecated addresses.
+ // TODO: Implement rule 4: Prefer home addresses.
+
+ // Rule 5: Prefer matching label.
+ if (span1.labelMatch != span2.labelMatch) {
+ return span2.labelMatch - span1.labelMatch;
+ }
+
+ // Rule 6: Prefer higher precedence.
+ if (span1.precedence != span2.precedence) {
+ return span2.precedence - span1.precedence;
+ }
+
+ // TODO: Implement rule 7: Prefer native transport.
+
+ // Rule 8: Prefer smaller scope.
+ if (span1.scope != span2.scope) {
+ return span1.scope - span2.scope;
+ }
+
+ // Rule 9: Use longest matching prefix. IPv6 only.
+ if (span1.prefixMatchLen != span2.prefixMatchLen) {
+ return span2.prefixMatchLen - span1.prefixMatchLen;
+ }
+
+ // Rule 10: Leave the order unchanged. Collections.sort is a stable sort.
+ return 0;
+ }
+ }
+
+ /**
+ * Class used to sort with RFC 6724
+ */
+ public static class SortableAddress {
+ public final int label;
+ public final int labelMatch;
+ public final int scope;
+ public final int scopeMatch;
+ public final int precedence;
+ public final int prefixMatchLen;
+ public final int hasSrcAddr;
+ public final InetAddress address;
+
+ public SortableAddress(@NonNull InetAddress addr, @Nullable InetAddress srcAddr) {
+ address = addr;
+ hasSrcAddr = (srcAddr != null) ? 1 : 0;
+ label = findLabel(addr);
+ scope = findScope(addr);
+ precedence = findPrecedence(addr);
+ labelMatch = ((srcAddr != null) && (label == findLabel(srcAddr))) ? 1 : 0;
+ scopeMatch = ((srcAddr != null) && (scope == findScope(srcAddr))) ? 1 : 0;
+ if (isIpv6Address(addr) && isIpv6Address(srcAddr)) {
+ prefixMatchLen = compareIpv6PrefixMatchLen(srcAddr, addr);
+ } else {
+ prefixMatchLen = 0;
+ }
+ }
+ }
+
+ /**
+ * Sort the given address list in RFC6724 order.
+ * Will leave the list unchanged if an error occurs.
+ *
+ * This function matches the behaviour of _rfc6724_sort in the native resolver.
+ */
+ public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network,
+ @NonNull List<InetAddress> answers) {
+ 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<>();
+ for (SortableAddress ans : sortableAnswerList) {
+ sortedAnswers.add(ans.address);
+ }
+
+ return sortedAnswers;
+ }
+
+ private static @Nullable InetAddress findSrcAddress(@Nullable Network network,
+ @NonNull InetAddress addr) {
+ final int domain;
+ if (isIpv4Address(addr)) {
+ domain = AF_INET;
+ } else if (isIpv6Address(addr)) {
+ domain = AF_INET6;
+ } else {
+ return null;
+ }
+ final FileDescriptor socket;
+ try {
+ socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "findSrcAddress:" + e.toString());
+ return null;
+ }
+ try {
+ if (network != null) network.bindSocket(socket);
+ Os.connect(socket, new InetSocketAddress(addr, 0));
+ return ((InetSocketAddress) Os.getsockname(socket)).getAddress();
+ } catch (IOException | ErrnoException e) {
+ return null;
+ } finally {
+ IoUtils.closeQuietly(socket);
+ }
+ }
+
+ /**
+ * Get the label for a given IPv4/IPv6 address.
+ * RFC 6724, section 2.1.
+ *
+ * Note that Java will return an IPv4-mapped address as an IPv4 address.
+ */
+ private static int findLabel(@NonNull InetAddress addr) {
+ if (isIpv4Address(addr)) {
+ return 4;
+ } else if (isIpv6Address(addr)) {
+ if (addr.isLoopbackAddress()) {
+ return 0;
+ } else if (isIpv6Address6To4(addr)) {
+ return 2;
+ } else if (isIpv6AddressTeredo(addr)) {
+ return 5;
+ } else if (isIpv6AddressULA(addr)) {
+ return 13;
+ } else if (((Inet6Address) addr).isIPv4CompatibleAddress()) {
+ return 3;
+ } else if (addr.isSiteLocalAddress()) {
+ return 11;
+ } else if (isIpv6Address6Bone(addr)) {
+ return 12;
+ } else {
+ // All other IPv6 addresses, including global unicast addresses.
+ return 1;
+ }
+ } else {
+ // This should never happen.
+ return 1;
+ }
+ }
+
+ private static boolean isIpv6Address(@Nullable InetAddress addr) {
+ return addr instanceof Inet6Address;
+ }
+
+ private static boolean isIpv4Address(@Nullable InetAddress addr) {
+ return addr instanceof Inet4Address;
+ }
+
+ private static boolean isIpv6Address6To4(@NonNull InetAddress addr) {
+ if (!isIpv6Address(addr)) return false;
+ final byte[] byteAddr = addr.getAddress();
+ return byteAddr[0] == 0x20 && byteAddr[1] == 0x02;
+ }
+
+ private static boolean isIpv6AddressTeredo(@NonNull InetAddress addr) {
+ if (!isIpv6Address(addr)) return false;
+ final byte[] byteAddr = addr.getAddress();
+ return byteAddr[0] == 0x20 && byteAddr[1] == 0x01 && byteAddr[2] == 0x00
+ && byteAddr[3] == 0x00;
+ }
+
+ private static boolean isIpv6AddressULA(@NonNull InetAddress addr) {
+ return isIpv6Address(addr) && (addr.getAddress()[0] & 0xfe) == 0xfc;
+ }
+
+ private static boolean isIpv6Address6Bone(@NonNull InetAddress addr) {
+ if (!isIpv6Address(addr)) return false;
+ final byte[] byteAddr = addr.getAddress();
+ return byteAddr[0] == 0x3f && byteAddr[1] == (byte) 0xfe;
+ }
+
+ private static int getIpv6MulticastScope(@NonNull InetAddress addr) {
+ return !isIpv6Address(addr) ? 0 : (addr.getAddress()[1] & 0x0f);
+ }
+
+ private static int findScope(@NonNull InetAddress addr) {
+ if (isIpv6Address(addr)) {
+ if (addr.isMulticastAddress()) {
+ return getIpv6MulticastScope(addr);
+ } else if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
+ /**
+ * RFC 4291 section 2.5.3 says loopback is to be treated as having
+ * link-local scope.
+ */
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ } else if (addr.isSiteLocalAddress()) {
+ return IPV6_ADDR_SCOPE_SITELOCAL;
+ } else {
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ }
+ } else if (isIpv4Address(addr)) {
+ if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
+ return IPV6_ADDR_SCOPE_LINKLOCAL;
+ } else {
+ /**
+ * RFC 6724 section 3.2. Other IPv4 addresses, including private addresses
+ * and shared addresses (100.64.0.0/10), are assigned global scope.
+ */
+ return IPV6_ADDR_SCOPE_GLOBAL;
+ }
+ } else {
+ /**
+ * This should never happen.
+ * Return a scope with low priority as a last resort.
+ */
+ return IPV6_ADDR_SCOPE_NODELOCAL;
+ }
+ }
+
+ /**
+ * Get the precedence for a given IPv4/IPv6 address.
+ * RFC 6724, section 2.1.
+ *
+ * Note that Java will return an IPv4-mapped address as an IPv4 address.
+ */
+ private static int findPrecedence(@NonNull InetAddress addr) {
+ if (isIpv4Address(addr)) {
+ return 35;
+ } else if (isIpv6Address(addr)) {
+ if (addr.isLoopbackAddress()) {
+ return 50;
+ } else if (isIpv6Address6To4(addr)) {
+ return 30;
+ } else if (isIpv6AddressTeredo(addr)) {
+ return 5;
+ } else if (isIpv6AddressULA(addr)) {
+ return 3;
+ } else if (((Inet6Address) addr).isIPv4CompatibleAddress() || addr.isSiteLocalAddress()
+ || isIpv6Address6Bone(addr)) {
+ return 1;
+ } else {
+ // All other IPv6 addresses, including global unicast addresses.
+ return 40;
+ }
+ } else {
+ return 1;
+ }
+ }
+
+ /**
+ * Find number of matching initial bits between the two addresses.
+ */
+ private static int compareIpv6PrefixMatchLen(@NonNull InetAddress srcAddr,
+ @NonNull InetAddress dstAddr) {
+ final byte[] srcByte = srcAddr.getAddress();
+ final byte[] dstByte = dstAddr.getAddress();
+
+ // This should never happen.
+ if (srcByte.length != dstByte.length) return 0;
+
+ for (int i = 0; i < dstByte.length; ++i) {
+ if (srcByte[i] == dstByte[i]) {
+ continue;
+ }
+ int x = BitUtils.uint8(srcByte[i]) ^ BitUtils.uint8(dstByte[i]);
+ return i * CHAR_BIT + (Integer.numberOfLeadingZeros(x) - 24); // Java ints are 32 bits
+ }
+ return dstByte.length * CHAR_BIT;
+ }
+
+ /**
+ * Check if given network has Ipv4 capability
+ * This function matches the behaviour of have_ipv4 in the native resolver.
+ */
+ public static boolean haveIpv4(@Nullable Network network) {
+ final SocketAddress addrIpv4 =
+ new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
+ return checkConnectivity(network, AF_INET, addrIpv4);
+ }
+
+ /**
+ * Check if given network has Ipv6 capability
+ * This function matches the behaviour of have_ipv6 in the native resolver.
+ */
+ public static boolean haveIpv6(@Nullable Network network) {
+ final SocketAddress addrIpv6 =
+ new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
+ return checkConnectivity(network, AF_INET6, addrIpv6);
+ }
+
+ private static boolean checkConnectivity(@Nullable Network network,
+ int domain, @NonNull SocketAddress addr) {
+ final FileDescriptor socket;
+ try {
+ socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+ } catch (ErrnoException e) {
+ return false;
+ }
+ try {
+ if (network != null) network.bindSocket(socket);
+ Os.connect(socket, addr);
+ } catch (IOException | ErrnoException e) {
+ return false;
+ } finally {
+ IoUtils.closeQuietly(socket);
+ }
+ return true;
+ }
+}
diff --git a/framework/src/android/net/util/KeepaliveUtils.java b/framework/src/android/net/util/KeepaliveUtils.java
new file mode 100644
index 0000000..bfc4563
--- /dev/null
+++ b/framework/src/android/net/util/KeepaliveUtils.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
+
+import com.android.internal.R;
+
+/**
+ * Collection of utilities for socket keepalive offload.
+ *
+ * @hide
+ */
+public final class KeepaliveUtils {
+
+ public static final String TAG = "KeepaliveUtils";
+
+ public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
+ public KeepaliveDeviceConfigurationException(final String msg) {
+ super(msg);
+ }
+ }
+
+ /**
+ * Read supported keepalive count for each transport type from overlay resource. This should be
+ * used to create a local variable store of resource customization, and use it as the input for
+ * {@link getSupportedKeepalivesForNetworkCapabilities}.
+ *
+ * @param context The context to read resource from.
+ * @return An array of supported keepalive count for each transport type.
+ */
+ @NonNull
+ public static int[] getSupportedKeepalives(@NonNull Context context) {
+ String[] res = null;
+ try {
+ res = context.getResources().getStringArray(
+ R.array.config_networkSupportedKeepaliveCount);
+ } catch (Resources.NotFoundException unused) {
+ }
+ if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
+
+ final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1];
+ for (final String row : res) {
+ if (TextUtils.isEmpty(row)) {
+ throw new KeepaliveDeviceConfigurationException("Empty string");
+ }
+ final String[] arr = row.split(",");
+ if (arr.length != 2) {
+ throw new KeepaliveDeviceConfigurationException("Invalid parameter length");
+ }
+
+ int transport;
+ int supported;
+ try {
+ transport = Integer.parseInt(arr[0]);
+ supported = Integer.parseInt(arr[1]);
+ } catch (NumberFormatException e) {
+ throw new KeepaliveDeviceConfigurationException("Invalid number format");
+ }
+
+ if (!NetworkCapabilities.isValidTransport(transport)) {
+ throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
+ }
+
+ if (supported < 0) {
+ throw new KeepaliveDeviceConfigurationException(
+ "Invalid supported count " + supported + " for "
+ + NetworkCapabilities.transportNameOf(transport));
+ }
+ ret[transport] = supported;
+ }
+ return ret;
+ }
+
+ /**
+ * Get supported keepalive count for the given {@link NetworkCapabilities}.
+ *
+ * @param supportedKeepalives An array of supported keepalive count for each transport type.
+ * @param nc The {@link NetworkCapabilities} of the network the socket keepalive is on.
+ *
+ * @return Supported keepalive count for the given {@link NetworkCapabilities}.
+ */
+ public static int getSupportedKeepalivesForNetworkCapabilities(
+ @NonNull int[] supportedKeepalives, @NonNull NetworkCapabilities nc) {
+ final int[] transports = nc.getTransportTypes();
+ if (transports.length == 0) return 0;
+ int supportedCount = supportedKeepalives[transports[0]];
+ // Iterate through transports and return minimum supported value.
+ for (final int transport : transports) {
+ if (supportedCount > supportedKeepalives[transport]) {
+ supportedCount = supportedKeepalives[transport];
+ }
+ }
+ return supportedCount;
+ }
+}
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
new file mode 100644
index 0000000..85e3fa3
--- /dev/null
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class to encapsulate management of the "Smart Networking" capability of
+ * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
+ * certain critical link failures occur.
+ *
+ * This enables the device to switch to another form of connectivity, like
+ * mobile, if it's available and working.
+ *
+ * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied
+ * Handler' whenever the computed "avoid bad wifi" value changes.
+ *
+ * Disabling this reverts the device to a level of networking sophistication
+ * circa 2012-13 by disabling disparate code paths each of which contribute to
+ * maintaining continuous, working Internet connectivity.
+ *
+ * @hide
+ */
+public class MultinetworkPolicyTracker {
+ private static String TAG = MultinetworkPolicyTracker.class.getSimpleName();
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Runnable mAvoidBadWifiCallback;
+ private final List<Uri> mSettingsUris;
+ private final ContentResolver mResolver;
+ private final SettingObserver mSettingObserver;
+ private final BroadcastReceiver mBroadcastReceiver;
+
+ private volatile boolean mAvoidBadWifi = true;
+ private volatile int mMeteredMultipathPreference;
+ private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ public MultinetworkPolicyTracker(Context ctx, Handler handler) {
+ this(ctx, handler, null);
+ }
+
+ public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
+ mContext = ctx;
+ mHandler = handler;
+ mAvoidBadWifiCallback = avoidBadWifiCallback;
+ mSettingsUris = Arrays.asList(
+ Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI),
+ Settings.Global.getUriFor(NETWORK_METERED_MULTIPATH_PREFERENCE));
+ mResolver = mContext.getContentResolver();
+ mSettingObserver = new SettingObserver();
+ mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reevaluateInternal();
+ }
+ };
+
+ ctx.getSystemService(TelephonyManager.class).listen(
+ new PhoneStateListener(handler.getLooper()) {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ mActiveSubId = subId;
+ reevaluateInternal();
+ }
+ }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+
+ updateAvoidBadWifi();
+ updateMeteredMultipathPreference();
+ }
+
+ public void start() {
+ for (Uri uri : mSettingsUris) {
+ mResolver.registerContentObserver(uri, false, mSettingObserver);
+ }
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter,
+ null /* broadcastPermission */, mHandler);
+
+ reevaluate();
+ }
+
+ public void shutdown() {
+ mResolver.unregisterContentObserver(mSettingObserver);
+
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+
+ public boolean getAvoidBadWifi() {
+ return mAvoidBadWifi;
+ }
+
+ // TODO: move this to MultipathPolicyTracker.
+ public int getMeteredMultipathPreference() {
+ return mMeteredMultipathPreference;
+ }
+
+ /**
+ * Whether the device or carrier configuration disables avoiding bad wifi by default.
+ */
+ public boolean configRestrictsAvoidBadWifi() {
+ return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+ }
+
+ @NonNull
+ private Resources getResourcesForActiveSubId() {
+ return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
+ }
+
+ /**
+ * Whether we should display a notification when wifi becomes unvalidated.
+ */
+ public boolean shouldNotifyWifiUnvalidated() {
+ return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null;
+ }
+
+ public String getAvoidBadWifiSetting() {
+ return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
+ }
+
+ @VisibleForTesting
+ public void reevaluate() {
+ 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() {
+ final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting());
+ final boolean prev = mAvoidBadWifi;
+ mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
+ return mAvoidBadWifi != prev;
+ }
+
+ /**
+ * The default (device and carrier-dependent) value for metered multipath preference.
+ */
+ public int configMeteredMultipathPreference() {
+ return mContext.getResources().getInteger(
+ R.integer.config_networkMeteredMultipathPreference);
+ }
+
+ public void updateMeteredMultipathPreference() {
+ String setting = Settings.Global.getString(mResolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
+ try {
+ mMeteredMultipathPreference = Integer.parseInt(setting);
+ } catch (NumberFormatException e) {
+ mMeteredMultipathPreference = configMeteredMultipathPreference();
+ }
+ }
+
+ private class SettingObserver extends ContentObserver {
+ public SettingObserver() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ Log.wtf(TAG, "Should never be reached.");
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (!mSettingsUris.contains(uri)) {
+ Log.wtf(TAG, "Unexpected settings observation: " + uri);
+ }
+ reevaluate();
+ }
+ }
+}
diff --git a/framework/src/android/net/util/SocketUtils.java b/framework/src/android/net/util/SocketUtils.java
new file mode 100644
index 0000000..e64060f
--- /dev/null
+++ b/framework/src/android/net/util/SocketUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkUtils;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketAddress;
+
+/**
+ * Collection of utilities to interact with raw sockets.
+ * @hide
+ */
+@SystemApi
+public final class SocketUtils {
+ /**
+ * Create a raw datagram socket that is bound to an interface.
+ *
+ * <p>Data sent through the socket will go directly to the underlying network, ignoring VPNs.
+ */
+ public static void bindSocketToInterface(@NonNull FileDescriptor socket, @NonNull String iface)
+ throws ErrnoException {
+ // SO_BINDTODEVICE actually takes a string. This works because the first member
+ // of struct ifreq is a NULL-terminated interface name.
+ // TODO: add a setsockoptString()
+ Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface);
+ NetworkUtils.protectFromVpn(socket);
+ }
+
+ /**
+ * Make a socket address to communicate with netlink.
+ */
+ @NonNull
+ public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) {
+ return new NetlinkSocketAddress(portId, groupsMask);
+ }
+
+ /**
+ * Make socket address that packet sockets can bind to.
+ *
+ * @param protocol the layer 2 protocol of the packets to receive. One of the {@code ETH_P_*}
+ * constants in {@link android.system.OsConstants}.
+ * @param ifIndex the interface index on which packets will be received.
+ */
+ @NonNull
+ public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) {
+ return new PacketSocketAddress(
+ protocol /* sll_protocol */,
+ ifIndex /* sll_ifindex */,
+ null /* sll_addr */);
+ }
+
+ /**
+ * Make a socket address that packet socket can send packets to.
+ * @deprecated Use {@link #makePacketSocketAddress(int, int, byte[])} instead.
+ *
+ * @param ifIndex the interface index on which packets will be sent.
+ * @param hwAddr the hardware address to which packets will be sent.
+ */
+ @Deprecated
+ @NonNull
+ public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) {
+ return new PacketSocketAddress(
+ 0 /* sll_protocol */,
+ ifIndex /* sll_ifindex */,
+ hwAddr /* sll_addr */);
+ }
+
+ /**
+ * Make a socket address that a packet socket can send packets to.
+ *
+ * @param protocol the layer 2 protocol of the packets to send. One of the {@code ETH_P_*}
+ * constants in {@link android.system.OsConstants}.
+ * @param ifIndex the interface index on which packets will be sent.
+ * @param hwAddr the hardware address to which packets will be sent.
+ */
+ @NonNull
+ public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex,
+ @NonNull byte[] hwAddr) {
+ return new PacketSocketAddress(
+ protocol /* sll_protocol */,
+ ifIndex /* sll_ifindex */,
+ hwAddr /* sll_addr */);
+ }
+
+ /**
+ * @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor)
+ */
+ public static void closeSocket(@Nullable FileDescriptor fd) throws IOException {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ }
+
+ private SocketUtils() {}
+}