OSDN Git Service

NetworkStatsService to adjust VPN stats before recording.
authorWenchao Tong <tongwenchao@google.com>
Wed, 4 Mar 2015 21:26:38 +0000 (13:26 -0800)
committerWenchao Tong <tongwenchao@google.com>
Fri, 13 Mar 2015 00:43:41 +0000 (17:43 -0700)
* Creates a new Parcelable class VpnInfo to hold required
  parameters for VPN stats adjustments.
* ConnectivityService to collect infomation and provide
  a list of VpnInfo, one for each user.
* NetworkStatsService passes the VpnInfo array to
  NetworkStatsRecorder.
* NetworkStatsRecorder calls NetworkStats.migrateTun()
  to do the math.
* Poll NetworkStats when the vpn application calls
  setUnderlyingNetworks().

Bug: 19536273
Change-Id: I7a4c7726b8243fead10416f7ec6eb5cf95f20183

core/java/android/net/IConnectivityManager.aidl
core/java/com/android/internal/net/VpnInfo.aidl [new file with mode: 0644]
core/java/com/android/internal/net/VpnInfo.java [new file with mode: 0644]
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/connectivity/Vpn.java
services/core/java/com/android/server/net/NetworkStatsRecorder.java
services/core/java/com/android/server/net/NetworkStatsService.java

index 46af112..da79b1a 100644 (file)
@@ -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 (file)
index 0000000..6fc97be
--- /dev/null
@@ -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 (file)
index 0000000..a676dac
--- /dev/null
@@ -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];
+        }
+    };
+}
index 7a9bbd6..8330e88 100644 (file)
@@ -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;
     }
 }
index ad38ab8..7f47678 100644 (file)
@@ -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;
index d5d7667..6490865 100644 (file)
@@ -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);
index 856a076..0b596aa 100644 (file)
@@ -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;