Merge "IpConn metrics: correctly read RA lifetimes" into nyc-mr1-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index 9d07e84..b0fb9f3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26063,6 +26063,7 @@
method public static void logStateEvent(java.lang.String, java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.metrics.DhcpClientEvent> CREATOR;
+ field public final int durationMs;
field public final java.lang.String ifName;
field public final java.lang.String msg;
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 5ac24d5..847d82b 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* 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
@@ -264,7 +266,7 @@
dest.writeParcelable(networkCapabilities, flags);
dest.writeInt(legacyType);
dest.writeInt(requestId);
- // type intentionally not preserved across process boundaries.
+ dest.writeString(type.name());
}
public static final Creator<NetworkRequest> CREATOR =
new Creator<NetworkRequest>() {
@@ -272,8 +274,8 @@
NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
int legacyType = in.readInt();
int requestId = in.readInt();
- // type intentionally not preserved across process boundaries.
- NetworkRequest result = new NetworkRequest(nc, legacyType, requestId, Type.NONE);
+ Type type = Type.valueOf(in.readString()); // IllegalArgumentException if invalid.
+ NetworkRequest result = new NetworkRequest(nc, legacyType, requestId, type);
return result;
}
public NetworkRequest[] newArray(int size) {
@@ -311,13 +313,10 @@
return (that.legacyType == this.legacyType &&
that.requestId == this.requestId &&
that.type == this.type &&
- ((that.networkCapabilities == null && this.networkCapabilities == null) ||
- (that.networkCapabilities != null &&
- that.networkCapabilities.equals(this.networkCapabilities))));
+ Objects.equals(that.networkCapabilities, this.networkCapabilities));
}
public int hashCode() {
- return requestId + (legacyType * 1013) +
- (networkCapabilities.hashCode() * 1051) + type.hashCode() * 17;
+ return Objects.hash(requestId, legacyType, networkCapabilities, type);
}
}
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 3fe68b4..169f571 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -21,36 +21,43 @@
import android.os.Parcelable;
/**
+ * An event recorded when a DhcpClient state machine transitions to a new state.
* {@hide}
*/
@SystemApi
public final class DhcpClientEvent implements Parcelable {
public final String ifName;
public final String msg;
+ public final int durationMs;
/** {@hide} */
- public DhcpClientEvent(String ifName, String msg) {
+ public DhcpClientEvent(String ifName, String msg, int durationMs) {
this.ifName = ifName;
this.msg = msg;
+ this.durationMs = durationMs;
}
private DhcpClientEvent(Parcel in) {
this.ifName = in.readString();
this.msg = in.readString();
+ this.durationMs = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeString(msg);
+ out.writeInt(durationMs);
}
+ @Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
- return String.format("DhcpClientEvent(%s, %s)", ifName, msg);
+ return String.format("DhcpClientEvent(%s, %s, %dms)", ifName, msg, durationMs);
}
public static final Parcelable.Creator<DhcpClientEvent> CREATOR
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3fb35d6..fb5b3f8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3836,6 +3836,13 @@
}
}
+ private void ensureNetworkRequestHasType(NetworkRequest request) {
+ if (request.type == NetworkRequest.Type.NONE) {
+ throw new IllegalArgumentException(
+ "All NetworkRequests in ConnectivityService must have a type");
+ }
+ }
+
/**
* Tracks info about the requester.
* Also used to notice when the calling process dies so we can self-expire
@@ -3851,7 +3858,7 @@
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
request = r;
- ensureRequestHasType();
+ ensureNetworkRequestHasType(request);
mPendingIntent = pi;
messenger = null;
mBinder = null;
@@ -3864,7 +3871,7 @@
super();
messenger = m;
request = r;
- ensureRequestHasType();
+ ensureNetworkRequestHasType(request);
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
@@ -3878,13 +3885,6 @@
}
}
- private void ensureRequestHasType() {
- if (request.type == NetworkRequest.Type.NONE) {
- throw new IllegalArgumentException(
- "All NetworkRequests in ConnectivityService must have a type");
- }
- }
-
private void enforceRequestCountLimit() {
synchronized (mUidToNetworkRequestCount) {
int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
@@ -4138,6 +4138,7 @@
@Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
+ ensureNetworkRequestHasType(networkRequest);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(),
0, networkRequest));
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 5852626..83cfc01 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -492,10 +492,19 @@
}
abstract class LoggingState extends State {
+ private long mEnterTimeMs;
+
@Override
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- mMetricsLog.log(new DhcpClientEvent(mIfaceName, getName()));
+ mEnterTimeMs = SystemClock.elapsedRealtime();
+ // TODO: record time for Init -> Bound and Bound -> Renewing -> Bound
+ }
+
+ @Override
+ public void exit() {
+ long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
+ mMetricsLog.log(new DhcpClientEvent(mIfaceName, getName(), (int) durationMs));
}
private String messageName(int what) {
@@ -520,6 +529,13 @@
}
return NOT_HANDLED;
}
+
+ @Override
+ public String getName() {
+ // All DhcpClient's states are inner classes with a well defined name.
+ // Use getSimpleName() and avoid super's getName() creating new String instances.
+ return getClass().getSimpleName();
+ }
}
// Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
@@ -546,10 +562,9 @@
}
}
- class StoppedState extends LoggingState {
+ class StoppedState extends State {
@Override
public boolean processMessage(Message message) {
- super.processMessage(message);
switch (message.what) {
case CMD_START_DHCP:
if (mRegisteredForPreDhcpNotification) {
@@ -578,10 +593,9 @@
}
}
- class DhcpState extends LoggingState {
+ class DhcpState extends State {
@Override
public void enter() {
- super.enter();
clearDhcpState();
if (initInterface() && initSockets()) {
mReceiveThread = new ReceiveThread();
@@ -679,7 +693,9 @@
}
}
+ @Override
public void exit() {
+ super.exit();
mKickAlarm.cancel();
mTimeoutAlarm.cancel();
}
@@ -784,15 +800,9 @@
}
}
- class DhcpHaveLeaseState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- }
-
+ class DhcpHaveLeaseState extends State {
@Override
public boolean processMessage(Message message) {
- super.processMessage(message);
switch (message.what) {
case CMD_EXPIRE_DHCP:
Log.d(TAG, "Lease expired!");
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 14744d0..d424717 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -1043,6 +1043,11 @@
* received. assertNoCallback may be called at any time.
*/
private class TestNetworkCallback extends NetworkCallback {
+ // Chosen to be much less than the linger timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger timeout.
+ private final static int TIMEOUT_MS = 50;
+
private class CallbackInfo {
public final CallbackState state;
public final Network network;
@@ -1297,7 +1302,7 @@
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
- // Cell is lingered because it would not satisfy any reques, even if it validated.
+ // Cell is lingered because it would not satisfy any request, even if it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
@@ -1327,6 +1332,29 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+ // The current code has a bug: if a network is lingering, and we add and then remove a
+ // request from it, we forget that the network was lingering and tear it down immediately.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ NetworkCallback noopCallback = new NetworkCallback();
+ mCm.requestNetwork(cellRequest, noopCallback);
+ mCm.unregisterNetworkCallback(noopCallback);
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultCallback);