diff options
7 files changed, 191 insertions, 25 deletions
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 46af112d1d49..da79b1a8f993 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -33,6 +33,7 @@ import android.os.ResultReceiver; 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 @@ interface IConnectivityManager 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 000000000000..6fc97be4095b --- /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 000000000000..a676dacb0c49 --- /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 7a9bbd6e51a5..8330e88339c3 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.ConnectivityManager.isNetworkTypeValid; 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.app.IBatteryStats; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub * 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * 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 class ConnectivityService extends IConnectivityManager.Stub 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 ad38ab800a32..7f47678d4f2f 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 android.util.Log; 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 @@ public class Vpn { 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 d5d76676309e..649086575c33 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.Log; 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 @@ public class NetworkStatsRecorder { * 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 @@ public class NetworkStatsRecorder { 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 856a07667d5c..0b596aa11900 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.SparseIntArray; 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { : 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { : 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; |