Merge "Only apply VPN isolation if it's fully routed"
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 6f0a4f9..9086d49 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -659,7 +659,8 @@
public abstract static class ConnectivityDiagnosticsCallback {
/**
* Called when the platform completes a data connectivity check. This will also be invoked
- * upon registration with the latest report.
+ * 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.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0f48ac2..6892a94 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -705,6 +705,36 @@
@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;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 5c754a1..8119df9 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -32,18 +34,52 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * A Utility class for handling for communicating between bearer-specific
+ * A utility class for handling for communicating between bearer-specific
* code and ConnectivityService.
*
+ * An agent manages the life cycle of a network. A network starts its
+ * life cycle when {@link register} is called on NetworkAgent. The network
+ * is then connecting. When full L3 connectivity has been established,
+ * the agent shoud call {@link setConnected} 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
@@ -65,7 +101,7 @@
private final String LOG_TAG;
private static final boolean DBG = true;
private static final boolean VDBG = false;
- private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+ private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500;
private boolean mBandwidthUpdateScheduled = false;
@@ -74,6 +110,8 @@
// into the internal API of ConnectivityService.
@NonNull
private NetworkInfo mNetworkInfo;
+ @NonNull
+ private final Object mRegisterLock = new Object();
/**
* The ID of the {@link NetworkProvider} that created this object, or
@@ -158,6 +196,14 @@
*/
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;
@@ -202,7 +248,7 @@
* Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
* periodically on the given interval.
*
- * arg1 = the slot number of the keepalive to start
+ * arg1 = the hardware slot number of the keepalive to start
* arg2 = interval in seconds
* obj = KeepalivePacketData object describing the data to be sent
*
@@ -214,7 +260,7 @@
/**
* Requests that the specified keepalive packet be stopped.
*
- * arg1 = slot number of the keepalive to stop.
+ * arg1 = hardware slot number of the keepalive to stop.
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
* @hide
@@ -229,7 +275,7 @@
* This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
* so that the app's {@link SocketKeepalive.Callback} methods can be called.
*
- * arg1 = slot number of the keepalive
+ * arg1 = hardware slot number of the keepalive
* arg2 = error code
* @hide
*/
@@ -259,7 +305,7 @@
* remote site will send ACK packets in response to the keepalive packets, the firmware also
* needs to be configured to properly filter the ACKs to prevent the system from waking up.
* This does not happen with UDP, so this message is TCP-specific.
- * arg1 = slot number of the keepalive to filter for.
+ * arg1 = hardware slot number of the keepalive to filter for.
* obj = the keepalive packet to send repeatedly.
* @hide
*/
@@ -268,7 +314,7 @@
/**
* Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
* {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
- * arg1 = slot number of the keepalive packet filter to remove.
+ * arg1 = hardware slot number of the keepalive packet filter to remove.
* @hide
*/
public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
@@ -441,7 +487,15 @@
+ (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+ redirectUrl);
}
- onValidationStatus(msg.arg1 /* status */, 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: {
@@ -489,19 +543,29 @@
/**
* 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");
final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
.getSystemService(Context.CONNECTIVITY_SERVICE);
- mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
- new NetworkInfo(mInitialConfiguration.info),
- mInitialConfiguration.properties, mInitialConfiguration.capabilities,
- mInitialConfiguration.score, mInitialConfiguration.config, providerId);
- mInitialConfiguration = null; // All this memory can now be GC'd
+ synchronized (mRegisterLock) {
+ if (mNetwork != null) {
+ throw new IllegalStateException("Agent already registered");
+ }
+ mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
+ new NetworkInfo(mInitialConfiguration.info),
+ mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+ mInitialConfiguration.score, mInitialConfiguration.config, providerId);
+ mInitialConfiguration = null; // All this memory can now be GC'd
+ }
return mNetwork;
}
@@ -544,13 +608,14 @@
* Must be called by the agent when the network's {@link LinkProperties} change.
* @param linkProperties the new LinkProperties.
*/
- public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+ public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
Objects.requireNonNull(linkProperties);
queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
* Inform ConnectivityService that this agent has now connected.
+ * Call {@link #unregister} to disconnect.
*/
public void setConnected() {
if (mIsLegacy) {
@@ -569,8 +634,7 @@
*/
public void unregister() {
if (mIsLegacy) {
- throw new UnsupportedOperationException(
- "Legacy agents can't call unregister.");
+ throw new UnsupportedOperationException("Legacy agents can't call unregister.");
}
mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
@@ -626,7 +690,7 @@
* @hide TODO: expose something better.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public void sendNetworkInfo(NetworkInfo networkInfo) {
+ public final void sendNetworkInfo(NetworkInfo networkInfo) {
if (!mIsLegacy) {
throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
}
@@ -637,7 +701,7 @@
* Must be called by the agent when the network's {@link NetworkCapabilities} change.
* @param networkCapabilities the new NetworkCapabilities.
*/
- public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(networkCapabilities);
mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
@@ -647,9 +711,10 @@
/**
* Must be called by the agent to update the score of this network.
- * @param score the new score.
+ *
+ * @param score the new score, between 0 and 99.
*/
- public void sendNetworkScore(int score) {
+ public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
if (score < 0) {
throw new IllegalArgumentException("Score must be >= 0");
}
@@ -737,11 +802,11 @@
* subsequent attempts to validate connectivity that fail.
*
* @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
- * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal),
+ * @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(int status, @Nullable String redirectUrl) {
- networkStatus(status, redirectUrl);
+ public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
+ networkStatus(status, redirectUri.toString());
}
/** @hide TODO delete once subclasses have moved to onValidationStatus */
protected void networkStatus(int status, String redirectUrl) {
@@ -770,7 +835,12 @@
* @param intervalSeconds the interval between packets
* @param packet the packet to send.
*/
- public void onStartSocketKeepalive(int slot, int intervalSeconds,
+ // 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,
+ @IntRange(from = 10, to = 3600) int intervalSeconds,
@NonNull KeepalivePacketData packet) {
Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds,
packet);
@@ -801,9 +871,11 @@
* 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.
+ * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
+ * or SocketKeepalive.SUCCESS constants.
*/
- public void sendSocketKeepaliveEvent(int slot, int event) {
+ public final void sendSocketKeepaliveEvent(int slot,
+ @SocketKeepalive.KeepaliveEvent int event) {
queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
}
/** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
@@ -845,9 +917,18 @@
}
/**
- * Called by ConnectivityService to inform this network transport of signal strength thresholds
+ * Called by ConnectivityService to inform this network agent of signal strength thresholds
* that when crossed should trigger a system wakeup and a NetworkCapabilities update.
*
+ * When the system updates the list of thresholds that should wake up the CPU for a
+ * given agent it will call this method on the agent. The agent that implement this
+ * should implement it in hardware so as to ensure the CPU will be woken up on breach.
+ * Agents are expected to react to a breach by sending an updated NetworkCapabilities
+ * object with the appropriate signal strength to sendNetworkCapabilities.
+ *
+ * The specific units are bearer-dependent. See details on the units and requests in
+ * {@link NetworkCapabilities.Builder#setSignalStrength}.
+ *
* @param thresholds the array of thresholds that should trigger wakeups.
*/
public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index ca9328a..fee868a 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -155,6 +155,7 @@
/**
* @return the legacy type
*/
+ @ConnectivityManager.LegacyNetworkType
public int getLegacyType() {
return legacyType;
}
@@ -206,7 +207,7 @@
/**
* Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
*/
- public static class Builder {
+ public static final class Builder {
private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
/**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ad1e501..995cb72 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -16,6 +16,8 @@
package android.net;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -118,7 +120,7 @@
mTransportInfo = nc.mTransportInfo;
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
- setAdministratorUids(nc.mAdministratorUids);
+ setAdministratorUids(nc.getAdministratorUids());
mOwnerUid = nc.mOwnerUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
@@ -919,18 +921,22 @@
* <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.
*/
- private int[] mAdministratorUids = new int[0];
+ @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
+ * <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.
@@ -938,19 +944,34 @@
* <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
@@ -961,6 +982,40 @@
}
/**
+ * 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
*/
@@ -1455,6 +1510,7 @@
combineUids(nc);
combineSSIDs(nc);
combineRequestor(nc);
+ combineAdministratorUids(nc);
}
/**
@@ -1568,7 +1624,8 @@
&& equalsUids(that)
&& equalsSSID(that)
&& equalsPrivateDnsBroken(that)
- && equalsRequestor(that);
+ && equalsRequestor(that)
+ && equalsAdministratorUids(that);
}
@Override
@@ -1588,7 +1645,8 @@
+ Objects.hashCode(mTransportInfo) * 41
+ Objects.hashCode(mPrivateDnsBroken) * 43
+ Objects.hashCode(mRequestorUid) * 47
- + Objects.hashCode(mRequestorPackageName) * 53;
+ + Objects.hashCode(mRequestorPackageName) * 53
+ + Arrays.hashCode(mAdministratorUids) * 59;
}
@Override
@@ -1609,7 +1667,7 @@
dest.writeArraySet(mUids);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
- dest.writeIntArray(mAdministratorUids);
+ dest.writeIntArray(getAdministratorUids());
dest.writeInt(mOwnerUid);
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 418d691..75086cf 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -33,8 +34,8 @@
* {@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 #onRequestWithdrawn} to
- * receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the
+ * 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.
*
@@ -77,7 +78,7 @@
* Constructs a new NetworkProvider.
*
* @param looper the Looper on which to run {@link #onNetworkRequested} and
- * {@link #onRequestWithdrawn}.
+ * {@link #onNetworkRequestWithdrawn}.
* @param name the name of the listener, used only for debugging.
*
* @hide
@@ -94,7 +95,7 @@
onNetworkRequested((NetworkRequest) m.obj, m.arg1, m.arg2);
break;
case CMD_CANCEL_REQUEST:
- onRequestWithdrawn((NetworkRequest) m.obj);
+ onNetworkRequestWithdrawn((NetworkRequest) m.obj);
break;
default:
Log.e(mName, "Unhandled message: " + m.what);
@@ -142,14 +143,15 @@
* @hide
*/
@SystemApi
- public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {}
+ 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 onRequestWithdrawn(@NonNull NetworkRequest request) {}
+ public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {}
/**
* Asserts that no provider will ever be able to satisfy the specified request. The provider
@@ -157,7 +159,7 @@
* 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 cannot be fulfilled
+ * @param request the request that permanently cannot be fulfilled
* @hide
*/
@SystemApi
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index b0bf64e..75fe6b1 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -473,9 +473,7 @@
*
* @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
* satisfy any request.
- * @hide
*/
- @SystemApi
public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
return networkCapabilities.satisfiedByNetworkCapabilities(nc);
}
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index fc9a8f6..8ff8f4c 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -109,6 +109,16 @@
})
public @interface ErrorCode {}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ SUCCESS,
+ ERROR_INVALID_LENGTH,
+ ERROR_UNSUPPORTED,
+ ERROR_INSUFFICIENT_RESOURCES
+ })
+ public @interface KeepaliveEvent {}
+
/**
* The minimum interval in seconds between keepalive packet transmissions.
*
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index db0ea44..0c45285 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -653,8 +653,8 @@
final MultipathPolicyTracker mMultipathPolicyTracker;
@VisibleForTesting
- final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
- mConnectivityDiagnosticsCallbacks = new HashMap<>();
+ final Map<IBinder, ConnectivityDiagnosticsCallbackInfo> mConnectivityDiagnosticsCallbacks =
+ new HashMap<>();
/**
* Implements support for the legacy "one network per network type" model.
@@ -7827,11 +7827,12 @@
ensureRunningOnConnectivityServiceThread();
final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
+ final IBinder iCb = cb.asBinder();
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
// This means that the client registered the same callback multiple times. Do
// not override the previous entry, and exit silently.
- if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+ if (mConnectivityDiagnosticsCallbacks.containsKey(iCb)) {
if (VDBG) log("Diagnostics callback is already registered");
// Decrement the reference count for this NetworkRequestInfo. The reference count is
@@ -7841,40 +7842,75 @@
return;
}
- mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);
+ mConnectivityDiagnosticsCallbacks.put(iCb, cbInfo);
try {
- cb.asBinder().linkToDeath(cbInfo, 0);
+ iCb.linkToDeath(cbInfo, 0);
} catch (RemoteException e) {
cbInfo.binderDied();
+ return;
+ }
+
+ // Once registered, provide ConnectivityReports for matching Networks
+ final List<NetworkAgentInfo> matchingNetworks = new ArrayList<>();
+ synchronized (mNetworkForNetId) {
+ for (int i = 0; i < mNetworkForNetId.size(); i++) {
+ final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+ if (nai.satisfies(nri.request)) {
+ matchingNetworks.add(nai);
+ }
+ }
+ }
+ for (final NetworkAgentInfo nai : matchingNetworks) {
+ final ConnectivityReport report = nai.getConnectivityReport();
+ if (report == null) {
+ continue;
+ }
+ if (!checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ continue;
+ }
+
+ try {
+ cb.onConnectivityReportAvailable(report);
+ } catch (RemoteException e) {
+ // Exception while sending the ConnectivityReport. Move on to the next network.
+ }
}
}
private void handleUnregisterConnectivityDiagnosticsCallback(
@NonNull IConnectivityDiagnosticsCallback cb, int uid) {
ensureRunningOnConnectivityServiceThread();
+ final IBinder iCb = cb.asBinder();
- if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+ final ConnectivityDiagnosticsCallbackInfo cbInfo =
+ mConnectivityDiagnosticsCallbacks.remove(iCb);
+ if (cbInfo == null) {
if (VDBG) log("Removing diagnostics callback that is not currently registered");
return;
}
- final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
if (uid != nri.mUid) {
if (VDBG) loge("Different uid than registrant attempting to unregister cb");
return;
}
- cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
+ // Decrement the reference count for this NetworkRequestInfo. The reference count is
+ // incremented when the NetworkRequestInfo is created as part of
+ // enforceRequestCountLimit().
+ decrementNetworkRequestPerUidCount(nri);
+
+ iCb.unlinkToDeath(cbInfo, 0);
}
private void handleNetworkTestedWithExtras(
@NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
final NetworkAgentInfo nai = reportEvent.mNai;
final NetworkCapabilities networkCapabilities =
- new NetworkCapabilities(nai.networkCapabilities);
- clearNetworkCapabilitiesUids(networkCapabilities);
+ getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
final ConnectivityReport report =
new ConnectivityReport(
reportEvent.mNai.network,
@@ -7882,6 +7918,7 @@
nai.linkProperties,
networkCapabilities,
extras);
+ nai.setConnectivityReport(report);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
@@ -7897,8 +7934,7 @@
@NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
@NonNull PersistableBundle extras) {
final NetworkCapabilities networkCapabilities =
- new NetworkCapabilities(nai.networkCapabilities);
- clearNetworkCapabilitiesUids(networkCapabilities);
+ getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
final DataStallReport report =
new DataStallReport(
nai.network,
@@ -7931,23 +7967,25 @@
}
}
- private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
- nc.setUids(null);
- nc.setAdministratorUids(new int[0]);
- nc.setOwnerUid(Process.INVALID_UID);
+ private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
+ final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+ sanitized.setUids(null);
+ sanitized.setAdministratorUids(new int[0]);
+ sanitized.setOwnerUid(Process.INVALID_UID);
+ return sanitized;
}
private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
@NonNull NetworkAgentInfo nai) {
final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
- for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
+ for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
mConnectivityDiagnosticsCallbacks.entrySet()) {
final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
if (nai.satisfies(nri.request)) {
if (checkConnectivityDiagnosticsPermissions(
nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
- results.add(entry.getKey());
+ results.add(entry.getValue().mCb);
}
}
}
@@ -7984,11 +8022,7 @@
// Administrator UIDs also contains the Owner UID
final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
- for (final int uid : administratorUids) {
- if (uid == callbackUid) return true;
- }
-
- return false;
+ return ArrayUtils.contains(administratorUids, callbackUid);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index f844844..2d1f553 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -243,6 +244,10 @@
// How many of the satisfied requests are of type BACKGROUND_REQUEST.
private int mNumBackgroundNetworkRequests = 0;
+ // The last ConnectivityReport made available for this network. This value is only null before a
+ // report is generated. Once non-null, it will never be null again.
+ @Nullable private ConnectivityReport mConnectivityReport;
+
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -621,6 +626,32 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
+ /**
+ * Sets the most recent ConnectivityReport for this network.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ public void setConnectivityReport(@NonNull ConnectivityReport connectivityReport) {
+ mConnectivityReport = connectivityReport;
+ }
+
+ /**
+ * Returns the most recent ConnectivityReport for this network, or null if none have been
+ * reported yet.
+ *
+ * <p>This should only be called from the ConnectivityService thread.
+ *
+ * @hide
+ */
+ @Nullable
+ public ConnectivityReport getConnectivityReport() {
+ return mConnectivityReport;
+ }
+
+ // TODO: Print shorter members first and only print the boolean variable which value is true
+ // to improve readability.
public String toString() {
return "NetworkAgentInfo{"
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 916c339..316a83a 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -58,6 +58,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@@ -280,6 +281,7 @@
.addCapability(NET_CAPABILITY_NOT_METERED);
if (isAtLeastR()) {
netCap.setOwnerUid(123);
+ netCap.setAdministratorUids(new int[] {5, 11});
}
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
@@ -440,6 +442,23 @@
}
@Test
+ public void testSetAdministratorUids() {
+ NetworkCapabilities nc =
+ new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
+
+ assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
+ }
+
+ @Test
+ public void testSetAdministratorUidsWithDuplicates() {
+ try {
+ new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
+ fail("Expected IllegalArgumentException for duplicate uids");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
public void testCombineCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities();
NetworkCapabilities nc2 = new NetworkCapabilities();
@@ -491,6 +510,25 @@
assertFalse(nc2.appliesToUid(12));
assertTrue(nc1.appliesToUid(22));
assertTrue(nc2.appliesToUid(22));
+
+ final int[] adminUids = {3, 6, 12};
+ nc1.setAdministratorUids(adminUids);
+ nc2.combineCapabilities(nc1);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+ assertArrayEquals(nc2.getAdministratorUids(), adminUids);
+
+ final int[] adminUidsOtherOrder = {3, 12, 6};
+ nc1.setAdministratorUids(adminUids);
+ assertTrue(nc2.equalsAdministratorUids(nc1));
+
+ final int[] adminUids2 = {11, 1, 12, 3, 6};
+ nc1.setAdministratorUids(adminUids2);
+ assertFalse(nc2.equalsAdministratorUids(nc1));
+ assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
+ try {
+ nc2.combineCapabilities(nc1);
+ fail("Shouldn't be able to combine different lists of admin UIDs");
+ } catch (IllegalStateException expected) { }
}
@Test
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
new file mode 100644
index 0000000..9119d62
--- /dev/null
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.IFACE_VT
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.SET_FOREGROUND
+import android.net.NetworkStats.TAG_NONE
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsApiTest {
+ @Rule
+ @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
+ private val testStatsEmpty = NetworkStats(0L, 0)
+
+ // stats1 and stats2 will have some entries with common keys, which are expected to
+ // be merged if performing add on these 2 stats.
+ private val testStats1 = NetworkStats(0L, 0)
+ // Entries which only appear in set1.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
+
+ private val testStats2 = NetworkStats(0L, 0)
+ // Entries which are common for set1 and set2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+ // Entry which only appears in set2.
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+ // This is a result of adding stats1 and stats2, while the merging of common key items is
+ // subject to test later, this should not be initialized with for a loop to add stats1
+ // and stats2 above.
+ private val testStats3 = NetworkStats(0L, 9)
+ // Entries which are unique either in stats1 or stats2.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+ .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+ // Entries which are common for stats1 and stats2 are being merged.
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+ .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+ .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+ .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+
+ companion object {
+ private const val TEST_IFACE = "test0"
+ private const val TEST_UID1 = 1001
+ private const val TEST_UID2 = 1002
+ }
+
+ @Before
+ fun setUp() {
+ assertEquals(8, testStats1.size())
+ assertEquals(5, testStats2.size())
+ assertEquals(9, testStats3.size())
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testAddEntry() {
+ val expectedEntriesInStats2 = arrayOf(
+ Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
+ Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
+ Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
+ Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
+ Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+ // While testStats* are already initialized with addEntry, verify content added
+ // matches expectation.
+ for (i in expectedEntriesInStats2.indices) {
+ val entry = testStats2.getValues(i, null)
+ assertEquals(expectedEntriesInStats2[i], entry)
+ }
+
+ // Verify entry updated with addEntry.
+ val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
+ assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
+ stats.getValues(3, null))
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testAdd() {
+ var stats = NetworkStats(0L, 0)
+ assertNetworkStatsEquals(testStatsEmpty, stats)
+ stats = stats.add(testStats2)
+ assertNetworkStatsEquals(testStats2, stats)
+ stats = stats.add(testStats1)
+ // EMPTY + STATS2 + STATS1 = STATS3
+ assertNetworkStatsEquals(testStats3, stats)
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testParcelUnparcel() {
+ assertParcelingIsLossless(testStatsEmpty)
+ assertParcelingIsLossless(testStats1)
+ assertParcelingIsLossless(testStats2)
+ assertFieldCountEquals(15, NetworkStats::class.java)
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDescribeContents() {
+ assertEquals(0, testStatsEmpty.describeContents())
+ assertEquals(0, testStats1.describeContents())
+ assertEquals(0, testStats2.describeContents())
+ assertEquals(0, testStats3.describeContents())
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testSubtract() {
+ // STATS3 - STATS2 = STATS1
+ assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
+ // STATS3 - STATS1 = STATS2
+ assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testMethodsDontModifyReceiver() {
+ listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
+ val origStats = it.clone()
+ it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+ it.add(testStats3)
+ it.subtract(testStats1)
+ assertNetworkStatsEquals(origStats, it)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index e71d599..98f705f 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -503,6 +503,53 @@
}
@Test
+ public void testRemoveEmptyEntries() throws Exception {
+ // Test empty stats.
+ final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3);
+ assertEquals(0, statsEmpty.removeEmptyEntries().size());
+
+ // Test stats with non-zero entry.
+ final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1)
+ .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+ assertEquals(1, statsNonZero.size());
+ final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries();
+ assertEquals(1, expectedNonZero.size());
+ assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+
+ // Test stats with empty entry.
+ final NetworkStats statsZero = new NetworkStats(TEST_START, 1)
+ .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+ assertEquals(1, statsZero.size());
+ final NetworkStats expectedZero = statsZero.removeEmptyEntries();
+ assertEquals(1, statsZero.size()); // Assert immutable.
+ assertEquals(0, expectedZero.size());
+
+ // Test stats with multiple entries.
+ final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0)
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L);
+ assertEquals(9, statsMultiple.size());
+ final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries();
+ assertEquals(9, statsMultiple.size()); // Assert immutable.
+ assertEquals(7, expectedMultiple.size());
+ assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L);
+
+ // Test stats with multiple empty entries.
+ assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size());
+ assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size());
+ }
+
+ @Test
public void testClone() throws Exception {
final NetworkStats original = new NetworkStats(TEST_START, 5)
.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
index 8a4b533..ceca6f0 100644
--- a/tests/net/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -65,7 +65,7 @@
assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
assertFalse(p.isBypassable);
assertFalse(p.isMetered);
- assertEquals(1400, p.maxMtu);
+ assertEquals(1360, p.maxMtu);
assertFalse(p.areAuthParamsInline);
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 75c71bb..f2e39cf 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -316,6 +316,8 @@
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String INTERFACE_NAME = "interface";
+
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityService mService;
@@ -6747,16 +6749,12 @@
verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
verify(mConnectivityDiagnosticsCallback).asBinder();
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
verify(mIBinder, timeout(TIMEOUT_MS))
.unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
- assertFalse(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
}
@@ -6774,9 +6772,7 @@
verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
verify(mConnectivityDiagnosticsCallback).asBinder();
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
// Register the same callback again
mService.registerConnectivityDiagnosticsCallback(
@@ -6785,9 +6781,7 @@
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
- assertTrue(
- mService.mConnectivityDiagnosticsCallbacks.containsKey(
- mConnectivityDiagnosticsCallback));
+ assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
}
@Test
@@ -6914,6 +6908,38 @@
mContext.getOpPackageName()));
}
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
+ throws Exception {
+ // Set up the Network, which leads to a ConnectivityReport being cached for the network.
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties);
+ mCellNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callback.assertNoCallback();
+
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ verify(mConnectivityDiagnosticsCallback)
+ .onConnectivityReportAvailable(argThat(report -> {
+ return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName())
+ && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR);
+ }));
+ }
+
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);