Merge "NetworkStatsService to adjust VPN stats before recording."
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46af112..da79b1a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -33,6 +33,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
@@ -116,6 +117,8 @@
LegacyVpnInfo getLegacyVpnInfo();
+ VpnInfo[] getAllVpnInfo();
+
boolean updateLockdownVpn();
void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/com/android/internal/net/VpnInfo.aidl
new file mode 100644
index 0000000..6fc97be
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 com.android.internal.net;
+
+parcelable VpnInfo;
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java
new file mode 100644
index 0000000..a676dac
--- /dev/null
+++ b/core/java/com/android/internal/net/VpnInfo.java
@@ -0,0 +1,69 @@
+/*
+ * 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 com.android.internal.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A lightweight container used to carry information of the ongoing VPN.
+ * Internal use only..
+ *
+ * @hide
+ */
+public class VpnInfo implements Parcelable {
+ public int ownerUid;
+ public String vpnIface;
+ public String primaryUnderlyingIface;
+
+ @Override
+ public String toString() {
+ return "VpnInfo{" +
+ "ownerUid=" + ownerUid +
+ ", vpnIface='" + vpnIface + '\'' +
+ ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(ownerUid);
+ dest.writeString(vpnIface);
+ dest.writeString(primaryUnderlyingIface);
+ }
+
+ public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
+ @Override
+ public VpnInfo createFromParcel(Parcel source) {
+ VpnInfo info = new VpnInfo();
+ info.ownerUid = source.readInt();
+ info.vpnIface = source.readString();
+ info.primaryUnderlyingIface = source.readString();
+ return info;
+ }
+
+ @Override
+ public VpnInfo[] newArray(int size) {
+ return new VpnInfo[size];
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7a9bbd6..8330e88 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -100,6 +101,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.DctConstants;
import com.android.internal.util.AsyncChannel;
@@ -2978,7 +2980,6 @@
* Return the information of the ongoing legacy VPN. This method is used
* by VpnSettings and not available in ConnectivityManager. Permissions
* are checked in Vpn class.
- * @hide
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
@@ -2990,6 +2991,56 @@
}
/**
+ * Return the information of all ongoing VPNs. This method is used by NetworkStatsService
+ * and not available in ConnectivityManager.
+ */
+ @Override
+ public VpnInfo[] getAllVpnInfo() {
+ enforceConnectivityInternalPermission();
+ if (mLockdownEnabled) {
+ return new VpnInfo[0];
+ }
+
+ synchronized(mVpns) {
+ List<VpnInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mVpns.size(); i++) {
+ VpnInfo info = createVpnInfo(mVpns.valueAt(i));
+ if (info != null) {
+ infoList.add(info);
+ }
+ }
+ return infoList.toArray(new VpnInfo[infoList.size()]);
+ }
+ }
+
+ /**
+ * @return VPN information for accounting, or null if we can't retrieve all required
+ * information, e.g primary underlying iface.
+ */
+ @Nullable
+ private VpnInfo createVpnInfo(Vpn vpn) {
+ VpnInfo info = vpn.getVpnInfo();
+ if (info == null) {
+ return null;
+ }
+ Network[] underlyingNetworks = vpn.getUnderlyingNetworks();
+ // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
+ // the underlyingNetworks list.
+ if (underlyingNetworks == null) {
+ NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
+ info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
+ }
+ } else if (underlyingNetworks.length > 0) {
+ LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
+ if (linkProperties != null) {
+ info.primaryUnderlyingIface = linkProperties.getInterfaceName();
+ }
+ }
+ return info.primaryUnderlyingIface == null ? null : info;
+ }
+
+ /**
* Returns the information of the ongoing VPN. This method is used by VpnDialogs and
* not available in ConnectivityManager.
* Permissions are checked in Vpn class.
@@ -4512,8 +4563,13 @@
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
+ boolean success;
synchronized (mVpns) {
- return mVpns.get(user).setUnderlyingNetworks(networks);
+ success = mVpns.get(user).setUnderlyingNetworks(networks);
}
+ if (success) {
+ notifyIfacesChanged();
+ }
+ return success;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ad38ab8..7f47678 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -69,6 +69,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.server.net.BaseNetworkObserver;
@@ -808,6 +809,21 @@
return mConfig.underlyingNetworks;
}
+ /**
+ * This method should only be called by ConnectivityService. Because it doesn't
+ * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+ */
+ public synchronized VpnInfo getVpnInfo() {
+ if (!isRunningLocked()) {
+ return null;
+ }
+
+ VpnInfo info = new VpnInfo();
+ info.ownerUid = mOwnerUID;
+ info.vpnIface = mInterface;
+ return info;
+ }
+
public synchronized boolean appliesToUid(int uid) {
if (!isRunningLocked()) {
return false;
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index d5d7667..6490865 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -31,6 +31,7 @@
import android.util.MathUtils;
import android.util.Slog;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Sets;
@@ -163,7 +164,8 @@
* snapshot is considered bootstrap, and is not counted as delta.
*/
public void recordSnapshotLocked(NetworkStats snapshot,
- Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
+ Map<String, NetworkIdentitySet> ifaceIdent, VpnInfo[] vpnArray,
+ long currentTimeMillis) {
final HashSet<String> unknownIfaces = Sets.newHashSet();
// skip recording when snapshot missing
@@ -182,6 +184,12 @@
final long end = currentTimeMillis;
final long start = end - delta.getElapsedRealtime();
+ if (vpnArray != null) {
+ for (VpnInfo info : vpnArray) {
+ delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface);
+ }
+ }
+
NetworkStats.Entry entry = null;
for (int i = 0; i < delta.size(); i++) {
entry = delta.getValues(i, entry);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 856a076..0b596aa 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -111,6 +111,7 @@
import android.util.TrustedTime;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
@@ -855,6 +856,20 @@
return ident;
}
+ private void recordSnapshotLocked(long currentTime) throws RemoteException {
+ // snapshot and record current counters; read UID stats first to
+ // avoid overcounting dev stats.
+ final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+ final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
+ final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
+
+ VpnInfo[] vpnArray = mConnManager.getAllVpnInfo();
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, null, currentTime);
+ mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, null, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+ }
+
/**
* Bootstrap initial stats snapshot, usually during {@link #systemReady()}
* so we have baseline values without double-counting.
@@ -864,17 +879,7 @@
: System.currentTimeMillis();
try {
- // snapshot and record current counters; read UID stats first to
- // avoid overcounting dev stats.
- final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
- final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
- final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
-
- mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
- mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
- mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
- mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-
+ recordSnapshotLocked(currentTime);
} catch (IllegalStateException e) {
Slog.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
@@ -918,17 +923,7 @@
: System.currentTimeMillis();
try {
- // snapshot and record current counters; read UID stats first to
- // avoid overcounting dev stats.
- final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
- final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
- final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
-
- mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
- mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
- mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
- mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
-
+ recordSnapshotLocked(currentTime);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
return;