summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/ConnectivityManager.java53
-rw-r--r--core/java/android/net/IConnectivityManager.aidl3
-rw-r--r--core/java/android/net/INetworkStatsService.aidl10
-rw-r--r--core/java/android/net/NetworkState.aidl19
-rw-r--r--core/java/android/net/NetworkState.java68
-rw-r--r--core/java/android/net/NetworkStatsHistory.java36
-rw-r--r--core/java/android/net/TrafficStats.java36
-rw-r--r--core/java/com/android/internal/util/Objects.java45
-rw-r--r--core/java/com/android/internal/util/Preconditions.java57
-rw-r--r--core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java20
-rw-r--r--services/java/com/android/server/ConnectivityService.java73
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/java/com/android/server/net/InterfaceIdentity.java73
-rw-r--r--services/java/com/android/server/net/NetworkIdentity.java208
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java2
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java247
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java47
17 files changed, 789 insertions, 209 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c72c4b0..21cce44 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -256,10 +256,61 @@ public class ConnectivityManager {
private final IConnectivityManager mService;
- static public boolean isNetworkTypeValid(int networkType) {
+ public static boolean isNetworkTypeValid(int networkType) {
return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
}
+ /** {@hide} */
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
+ case TYPE_MOBILE:
+ return "MOBILE";
+ case TYPE_WIFI:
+ return "WIFI";
+ case TYPE_MOBILE_MMS:
+ return "MOBILE_MMS";
+ case TYPE_MOBILE_SUPL:
+ return "MOBILE_SUPL";
+ case TYPE_MOBILE_DUN:
+ return "MOBILE_DUN";
+ case TYPE_MOBILE_HIPRI:
+ return "MOBILE_HIPRI";
+ case TYPE_WIMAX:
+ return "WIMAX";
+ case TYPE_BLUETOOTH:
+ return "BLUETOOTH";
+ case TYPE_DUMMY:
+ return "DUMMY";
+ case TYPE_ETHERNET:
+ return "ETHERNET";
+ case TYPE_MOBILE_FOTA:
+ return "MOBILE_FOTA";
+ case TYPE_MOBILE_IMS:
+ return "MOBILE_IMS";
+ case TYPE_MOBILE_CBS:
+ return "MOBILE_CBS";
+ default:
+ return Integer.toString(type);
+ }
+ }
+
+ /** {@hide} */
+ public static boolean isNetworkTypeMobile(int networkType) {
+ switch (networkType) {
+ case TYPE_MOBILE:
+ case TYPE_MOBILE_MMS:
+ case TYPE_MOBILE_SUPL:
+ case TYPE_MOBILE_DUN:
+ case TYPE_MOBILE_HIPRI:
+ case TYPE_MOBILE_FOTA:
+ case TYPE_MOBILE_IMS:
+ case TYPE_MOBILE_CBS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
public void setNetworkPreference(int preference) {
try {
mService.setNetworkPreference(preference);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 647a60a..07f6cec 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@ package android.net;
import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.NetworkState;
import android.net.ProxyProperties;
import android.os.IBinder;
@@ -40,6 +41,8 @@ interface IConnectivityManager
LinkProperties getActiveLinkProperties();
LinkProperties getLinkProperties(int networkType);
+ NetworkState[] getAllNetworkState();
+
boolean setRadios(boolean onOff);
boolean setRadio(int networkType, boolean turnOn);
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 6d57036..d38d16c 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -16,12 +16,18 @@
package android.net;
+import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
/** {@hide} */
interface INetworkStatsService {
- NetworkStatsHistory[] getNetworkStatsSummary(int networkType);
- NetworkStatsHistory getNetworkStatsUid(int uid);
+ /** Return historical stats for traffic that matches template. */
+ NetworkStatsHistory getHistoryForNetwork(int networkTemplate);
+ /** Return historical stats for specific UID traffic that matches template. */
+ NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+
+ /** Return usage summary per UID for traffic that matches template. */
+ NetworkStats getSummaryPerUid(long start, long end, int networkTemplate);
}
diff --git a/core/java/android/net/NetworkState.aidl b/core/java/android/net/NetworkState.aidl
new file mode 100644
index 0000000..c0b6cdc
--- /dev/null
+++ b/core/java/android/net/NetworkState.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, 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;
+
+parcelable NetworkState;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
new file mode 100644
index 0000000..749039a
--- /dev/null
+++ b/core/java/android/net/NetworkState.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Snapshot of network state.
+ *
+ * @hide
+ */
+public class NetworkState implements Parcelable {
+
+ public final NetworkInfo networkInfo;
+ public final LinkProperties linkProperties;
+ public final LinkCapabilities linkCapabilities;
+
+ public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities) {
+ this.networkInfo = networkInfo;
+ this.linkProperties = linkProperties;
+ this.linkCapabilities = linkCapabilities;
+ }
+
+ public NetworkState(Parcel in) {
+ networkInfo = in.readParcelable(null);
+ linkProperties = in.readParcelable(null);
+ linkCapabilities = in.readParcelable(null);
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(networkInfo, flags);
+ out.writeParcelable(linkProperties, flags);
+ out.writeParcelable(linkCapabilities, flags);
+ }
+
+ public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+ public NetworkState createFromParcel(Parcel in) {
+ return new NetworkState(in);
+ }
+
+ public NetworkState[] newArray(int size) {
+ return new NetworkState[size];
+ }
+ };
+
+}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b16101f..60af475 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -40,26 +40,17 @@ import java.util.Arrays;
public class NetworkStatsHistory implements Parcelable {
private static final int VERSION = 1;
- /** {@link #uid} value when UID details unavailable. */
- public static final int UID_ALL = -1;
-
// TODO: teach about zigzag encoding to use less disk space
// TODO: teach how to convert between bucket sizes
- public final int networkType;
- public final String identity;
- public final int uid;
public final long bucketDuration;
- int bucketCount;
- long[] bucketStart;
- long[] rx;
- long[] tx;
+ public int bucketCount;
+ public long[] bucketStart;
+ public long[] rx;
+ public long[] tx;
- public NetworkStatsHistory(int networkType, String identity, int uid, long bucketDuration) {
- this.networkType = networkType;
- this.identity = identity;
- this.uid = uid;
+ public NetworkStatsHistory(long bucketDuration) {
this.bucketDuration = bucketDuration;
bucketStart = new long[0];
rx = new long[0];
@@ -68,9 +59,6 @@ public class NetworkStatsHistory implements Parcelable {
}
public NetworkStatsHistory(Parcel in) {
- networkType = in.readInt();
- identity = in.readString();
- uid = in.readInt();
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
rx = in.createLongArray();
@@ -80,9 +68,6 @@ public class NetworkStatsHistory implements Parcelable {
/** {@inheritDoc} */
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(networkType);
- out.writeString(identity);
- out.writeInt(uid);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, rx, bucketCount);
@@ -91,9 +76,6 @@ public class NetworkStatsHistory implements Parcelable {
public NetworkStatsHistory(DataInputStream in) throws IOException {
final int version = in.readInt();
- networkType = in.readInt();
- identity = in.readUTF();
- uid = in.readInt();
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
rx = readLongArray(in);
@@ -103,9 +85,6 @@ public class NetworkStatsHistory implements Parcelable {
public void writeToStream(DataOutputStream out) throws IOException {
out.writeInt(VERSION);
- out.writeInt(networkType);
- out.writeUTF(identity);
- out.writeInt(uid);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, rx, bucketCount);
@@ -214,11 +193,8 @@ public class NetworkStatsHistory implements Parcelable {
}
public void dump(String prefix, PrintWriter pw) {
- // TODO: consider stripping identity when dumping
pw.print(prefix);
- pw.print("NetworkStatsHistory: networkType="); pw.print(networkType);
- pw.print(" identity="); pw.print(identity);
- pw.print(" uid="); pw.println(uid);
+ pw.println("NetworkStatsHistory:");
for (int i = 0; i < bucketCount; i++) {
pw.print(prefix);
pw.print(" timestamp="); pw.print(bucketStart[i]);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8ab64fa..a0738c1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -41,6 +41,42 @@ public class TrafficStats {
*/
public final static int UNSUPPORTED = -1;
+ // TODO: find better home for these template constants
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together. Only uses statistics for currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_ALL = 1;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that roughly meet a "3G" definition, or lower. Only
+ * uses statistics for currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_3G_LOWER = 2;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that meet a "4G" definition. Only uses statistics for
+ * currently active IMSI.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_MOBILE_4G = 3;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
+ * networks together.
+ *
+ * @hide
+ */
+ public static final int TEMPLATE_WIFI = 4;
+
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
diff --git a/core/java/com/android/internal/util/Objects.java b/core/java/com/android/internal/util/Objects.java
index 598a079..2664182 100644
--- a/core/java/com/android/internal/util/Objects.java
+++ b/core/java/com/android/internal/util/Objects.java
@@ -16,34 +16,47 @@
package com.android.internal.util;
+import java.util.Arrays;
+
/**
* Object utility methods.
*/
public class Objects {
/**
- * Ensures the given object isn't {@code null}.
+ * Determines whether two possibly-null objects are equal. Returns:
+ *
+ * <ul>
+ * <li>{@code true} if {@code a} and {@code b} are both null.
+ * <li>{@code true} if {@code a} and {@code b} are both non-null and they are
+ * equal according to {@link Object#equals(Object)}.
+ * <li>{@code false} in all other situations.
+ * </ul>
*
- * @return the given object
- * @throws NullPointerException if the object is null
+ * <p>This assumes that any non-null objects passed to this function conform
+ * to the {@code equals()} contract.
*/
- public static <T> T nonNull(T t) {
- if (t == null) {
- throw new NullPointerException();
- }
- return t;
+ public static boolean equal(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
}
/**
- * Ensures the given object isn't {@code null}.
+ * Generates a hash code for multiple values. The hash code is generated by
+ * calling {@link Arrays#hashCode(Object[])}.
*
- * @return the given object
- * @throws NullPointerException if the object is null
+ * <p>This is useful for implementing {@link Object#hashCode()}. For example,
+ * in an object that has three properties, {@code x}, {@code y}, and
+ * {@code z}, one could write:
+ * <pre>
+ * public int hashCode() {
+ * return Objects.hashCode(getX(), getY(), getZ());
+ * }</pre>
+ *
+ * <b>Warning</b>: When a single object is supplied, the returned hash code
+ * does not equal the hash code of that object.
*/
- public static <T> T nonNull(T t, String message) {
- if (t == null) {
- throw new NullPointerException(message);
- }
- return t;
+ public static int hashCode(Object... objects) {
+ return Arrays.hashCode(objects);
}
+
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
new file mode 100644
index 0000000..a53a9c0
--- /dev/null
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 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.util;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ */
+public class Preconditions {
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference) {
+ if (reference == null) {
+ throw new NullPointerException();
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference, Object errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(String.valueOf(errorMessage));
+ }
+ return reference;
+ }
+
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index eb63c0d..69bbcd7 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.UID_ALL;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -51,7 +49,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRecordSingleBucket() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record data into narrow window to get single bucket
stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
@@ -62,7 +60,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRecordEqualBuckets() throws Exception {
final long bucketDuration = HOUR_IN_MILLIS;
- stats = buildStats(bucketDuration);
+ stats = new NetworkStatsHistory(bucketDuration);
// split equally across two buckets
final long recordStart = TEST_START + (bucketDuration / 2);
@@ -75,7 +73,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRecordTouchingBuckets() throws Exception {
final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// split almost completely into middle bucket, but with a few minutes
// overlap into neighboring buckets. total record is 20 minutes.
@@ -94,7 +92,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRecordGapBuckets() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data today and next week with large gap
final long firstStart = TEST_START;
@@ -122,7 +120,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRecordOverlapBuckets() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data in one bucket, and another overlapping buckets
stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
@@ -137,7 +135,7 @@ public class NetworkStatsHistoryTest extends TestCase {
public void testRemove() throws Exception {
final long BUCKET_SIZE = HOUR_IN_MILLIS;
- stats = buildStats(BUCKET_SIZE);
+ stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data across 24 buckets
stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
@@ -171,7 +169,7 @@ public class NetworkStatsHistoryTest extends TestCase {
// fuzzing with random events, looking for crashes
final Random r = new Random();
for (int i = 0; i < 500; i++) {
- stats = buildStats(r.nextLong());
+ stats = new NetworkStatsHistory(r.nextLong());
for (int j = 0; j < 10000; j++) {
if (r.nextBoolean()) {
// add range
@@ -191,10 +189,6 @@ public class NetworkStatsHistoryTest extends TestCase {
}
}
- private static NetworkStatsHistory buildStats(long bucketSize) {
- return new NetworkStatsHistory(TYPE_MOBILE, null, UID_ALL, bucketSize);
- }
-
private static void assertConsistent(NetworkStatsHistory stats) {
// verify timestamps are monotonic
for (int i = 1; i < stats.bucketCount; i++) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index b559fb9..e3d4c45 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -38,6 +39,7 @@ import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkState;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
@@ -65,6 +67,7 @@ import android.util.SparseIntArray;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
+import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -563,25 +566,32 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private boolean isNetworkBlocked(NetworkInfo info, int uid) {
synchronized (mUidRules) {
- return isNetworkBlockedLocked(info, uid);
+ // TODO: expand definition of "paid" network to cover tethered or
+ // paid hotspot use cases.
+ final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+ final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+ if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+ return true;
+ }
+
+ // no restrictive rules; network is visible
+ return false;
}
}
/**
- * Check if UID is blocked from using the given {@link NetworkInfo}.
+ * Return a filtered version of the given {@link NetworkInfo}, potentially
+ * marked {@link DetailedState#BLOCKED} based on
+ * {@link #isNetworkBlocked(NetworkInfo, int)}.
*/
- private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
- // TODO: expand definition of "paid" network to cover tethered or paid
- // hotspot use cases.
- final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
- final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
-
- if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
- return true;
+ private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
+ if (isNetworkBlocked(info, uid)) {
+ // network is blocked; clone and override state
+ info = new NetworkInfo(info);
+ info.setDetailedState(DetailedState.BLOCKED, null, null);
}
-
- // no restrictive rules; network is visible
- return false;
+ return info;
}
/**
@@ -616,12 +626,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- info = tracker.getNetworkInfo();
- if (isNetworkBlocked(info, uid)) {
- // network is blocked; clone and override state
- info = new NetworkInfo(info);
- info.setDetailedState(DetailedState.BLOCKED, null, null);
- }
+ info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
}
}
return info;
@@ -631,22 +636,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
- final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
- int i = 0;
+ final ArrayList<NetworkInfo> result = Lists.newArrayList();
synchronized (mUidRules) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- NetworkInfo info = tracker.getNetworkInfo();
- if (isNetworkBlockedLocked(info, uid)) {
- // network is blocked; clone and override state
- info = new NetworkInfo(info);
- info.setDetailedState(DetailedState.BLOCKED, null, null);
- }
- result[i++] = info;
+ result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
}
}
}
- return result;
+ return result.toArray(new NetworkInfo[result.size()]);
}
/**
@@ -674,6 +672,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return null;
}
+ @Override
+ public NetworkState[] getAllNetworkState() {
+ enforceAccessPermission();
+ final int uid = Binder.getCallingUid();
+ final ArrayList<NetworkState> result = Lists.newArrayList();
+ synchronized (mUidRules) {
+ for (NetworkStateTracker tracker : mNetTrackers) {
+ if (tracker != null) {
+ final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ result.add(new NetworkState(
+ info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+ }
+ }
+ }
+ return result.toArray(new NetworkState[result.size()]);
+ }
+
public boolean setRadios(boolean turnOn) {
boolean result = true;
enforceChangePermission();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 596cbac..fd03e5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -305,6 +305,7 @@ class ServerThread extends Thread {
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+ networkStats.bindConnectivityManager(connectivity);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Connectivity Service", e);
}
diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java
new file mode 100644
index 0000000..ff86581
--- /dev/null
+++ b/services/java/com/android/server/net/InterfaceIdentity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.server.net;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.HashSet;
+
+/**
+ * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
+ * active on that interface.
+ *
+ * @hide
+ */
+public class InterfaceIdentity extends HashSet<NetworkIdentity> {
+ private static final int VERSION_CURRENT = 1;
+
+ public InterfaceIdentity() {
+ }
+
+ public InterfaceIdentity(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_CURRENT: {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ add(new NetworkIdentity(in));
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void writeToStream(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_CURRENT);
+ out.writeInt(size());
+ for (NetworkIdentity ident : this) {
+ ident.writeToStream(out);
+ }
+ }
+
+ /**
+ * Test if any {@link NetworkIdentity} on this interface matches the given
+ * template and IMEI.
+ */
+ public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+ for (NetworkIdentity ident : this) {
+ if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
new file mode 100644
index 0000000..79feb95
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkIdentity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 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.server.net;
+
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.getNetworkClass;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Objects;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+
+/**
+ * Identity of a {@link NetworkInfo}, defined by network type and billing
+ * relationship (such as IMSI).
+ *
+ * @hide
+ */
+public class NetworkIdentity {
+ private static final int VERSION_CURRENT = 1;
+
+ public final int type;
+ public final int subType;
+ public final String subscriberId;
+
+ public NetworkIdentity(int type, int subType, String subscriberId) {
+ this.type = type;
+ this.subType = subType;
+ this.subscriberId = subscriberId;
+ }
+
+ public NetworkIdentity(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_CURRENT: {
+ type = in.readInt();
+ subType = in.readInt();
+ subscriberId = in.readUTF();
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void writeToStream(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_CURRENT);
+ out.writeInt(type);
+ out.writeInt(subType);
+ out.writeUTF(subscriberId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(type, subType, subscriberId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkIdentity) {
+ final NetworkIdentity ident = (NetworkIdentity) obj;
+ return type == ident.type && subType == ident.subType
+ && Objects.equal(subscriberId, ident.subscriberId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ final String subTypeName;
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ subTypeName = TelephonyManager.getNetworkTypeName(subType);
+ } else {
+ subTypeName = Integer.toString(subType);
+ }
+
+ return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]";
+ }
+
+ /**
+ * Test if this network matches the given template and IMEI.
+ */
+ public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+ switch (networkTemplate) {
+ case TEMPLATE_MOBILE_ALL:
+ return matchesMobile(subscriberId);
+ case TEMPLATE_MOBILE_3G_LOWER:
+ return matchesMobile3gLower(subscriberId);
+ case TEMPLATE_MOBILE_4G:
+ return matchesMobile4g(subscriberId);
+ case TEMPLATE_WIFI:
+ return matchesWifi();
+ default:
+ throw new IllegalArgumentException("unknown network template");
+ }
+ }
+
+ /**
+ * Check if mobile network with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile(String subscriberId) {
+ if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) {
+ return true;
+ } else if (type == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 3G or lower with matching IMEI.
+ */
+ private boolean matchesMobile3gLower(String subscriberId) {
+ if (isNetworkTypeMobile(type)
+ && Objects.equal(this.subscriberId, subscriberId)) {
+ switch (getNetworkClass(subType)) {
+ case NETWORK_CLASS_2_G:
+ case NETWORK_CLASS_3_G:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 4G with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile4g(String subscriberId) {
+ if (isNetworkTypeMobile(type)
+ && Objects.equal(this.subscriberId, subscriberId)) {
+ switch (getNetworkClass(subType)) {
+ case NETWORK_CLASS_4_G:
+ return true;
+ }
+ } else if (type == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if matches Wi-Fi network template.
+ */
+ private boolean matchesWifi() {
+ if (type == TYPE_WIFI) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Build a {@link NetworkIdentity} from the given {@link NetworkState},
+ * assuming that any mobile networks are using the current IMSI.
+ */
+ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+ final int type = state.networkInfo.getType();
+ final int subType = state.networkInfo.getSubtype();
+
+ // TODO: consider moving subscriberId over to LinkCapabilities, so it
+ // comes from an authoritative source.
+
+ final String subscriberId;
+ if (isNetworkTypeMobile(type)) {
+ final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ subscriberId = telephony.getSubscriberId();
+ } else {
+ subscriberId = null;
+ }
+ return new NetworkIdentity(type, subType, subscriberId);
+ }
+
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 17c7161..1a90a92 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -89,6 +89,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// TODO: keep record of billing cycle details, and limit rules
// TODO: keep map of interfaces-to-billing-relationship
+ // TODO: dispatch callbacks through handler when locked
+
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats) {
mContext = checkNotNull(context, "missing context");
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index d9c1f25..66d1274 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2011 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.
@@ -16,12 +16,13 @@
package com.android.server.net;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.SHUTDOWN;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.UID_ALL;
+import static com.android.internal.util.Preconditions.checkNotNull;
import android.app.AlarmManager;
import android.app.IAlarmManager;
@@ -30,11 +31,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.IConnectivityManager;
import android.net.INetworkStatsService;
-import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
-import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
@@ -46,12 +48,12 @@ import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.TrustedTime;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
+import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -67,6 +69,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final IAlarmManager mAlarmManager;
private final TrustedTime mTime;
+ private IConnectivityManager mConnManager;
+
private static final String ACTION_NETWORK_STATS_POLL =
"com.android.server.action.NETWORK_STATS_POLL";
@@ -94,9 +98,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final Object mStatsLock = new Object();
/** Set of active ifaces during this boot. */
- private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap();
+ private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
/** Set of historical stats for known ifaces. */
- private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap();
+ private HashMap<InterfaceIdentity, NetworkStatsHistory> mStats = Maps.newHashMap();
private NetworkStats mLastPollStats;
private NetworkStats mLastPersistStats;
@@ -132,13 +136,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
readStatsLocked();
// watch other system services that claim interfaces
- // TODO: protect incoming broadcast with permissions check.
- // TODO: consider migrating this to ConnectivityService, but it might
- // cause a circular dependency.
- final IntentFilter interfaceFilter = new IntentFilter();
- interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- mContext.registerReceiver(mInterfaceReceiver, interfaceFilter);
+ final IntentFilter ifaceFilter = new IntentFilter();
+ ifaceFilter.addAction(CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -155,6 +155,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
+ public void bindConnectivityManager(IConnectivityManager connManager) {
+ mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+ }
+
/**
* Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
* reschedule based on current {@link #POLL_INTERVAL} value.
@@ -173,45 +177,54 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
- public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) {
- // TODO: return history for requested types
- return null;
+ public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ synchronized (mStatsLock) {
+ // combine all interfaces that match template
+ final String subscriberId = getActiveSubscriberId();
+ final NetworkStatsHistory combined = new NetworkStatsHistory(SUMMARY_BUCKET_DURATION);
+ for (InterfaceIdentity ident : mStats.keySet()) {
+ final NetworkStatsHistory history = mStats.get(ident);
+ if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ // TODO: combine all matching history data into a single history
+ }
+ }
+ return combined;
+ }
}
@Override
- public NetworkStatsHistory getNetworkStatsUid(int uid) {
+ public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
// TODO: return history for requested uid
return null;
}
+ @Override
+ public NetworkStats getSummaryPerUid(long start, long end, int networkTemplate) {
+ // TODO: create relaxed permission for reading stats
+ mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+ // TODO: total UID-granularity usage between time range
+ return null;
+ }
+
/**
- * Receiver that watches for other system components that claim network
+ * Receiver that watches for {@link IConnectivityManager} to claim network
* interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
* with mobile interfaces.
*/
- private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) {
- final LinkProperties prop = intent.getParcelableExtra(
- Phone.DATA_LINK_PROPERTIES_KEY);
- final String iface = prop != null ? prop.getInterfaceName() : null;
- if (iface != null) {
- final TelephonyManager teleManager = (TelephonyManager) context
- .getSystemService(Context.TELEPHONY_SERVICE);
- final InterfaceInfo info = new InterfaceInfo(
- iface, TYPE_MOBILE, teleManager.getSubscriberId());
- reportActiveInterface(info);
- }
- } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
- final LinkProperties prop = intent.getParcelableExtra(
- WifiManager.EXTRA_LINK_PROPERTIES);
- final String iface = prop != null ? prop.getInterfaceName() : null;
- if (iface != null) {
- final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null);
- reportActiveInterface(info);
- }
+ // on background handler thread, and verified CONNECTIVITY_INTERNAL
+ // permission above.
+ synchronized (mStatsLock) {
+ updateIfacesLocked();
}
}
};
@@ -219,8 +232,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // already running on background handler, network/io is safe, and
- // caller verified to have UPDATE_DEVICE_STATS permission above.
+ // on background handler thread, and verified UPDATE_DEVICE_STATS
+ // permission above.
synchronized (mStatsLock) {
// TODO: acquire wakelock while performing poll
performPollLocked();
@@ -231,13 +244,49 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // persist stats during clean shutdown
+ // verified SHUTDOWN permission above.
synchronized (mStatsLock) {
writeStatsLocked();
}
}
};
+ /**
+ * Inspect all current {@link NetworkState} to derive mapping from {@code
+ * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
+ * are active on a single {@code iface}, they are combined under a single
+ * {@link InterfaceIdentity}.
+ */
+ private void updateIfacesLocked() {
+ if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+
+ // take one last stats snapshot before updating iface mapping. this
+ // isn't perfect, since the kernel may already be counting traffic from
+ // the updated network.
+ // TODO: verify that we only poll summary stats, not uid details
+ performPollLocked();
+
+ final NetworkState[] states;
+ try {
+ states = mConnManager.getAllNetworkState();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading network state");
+ return;
+ }
+
+ // rebuild active interfaces based on connected networks
+ mActiveIface.clear();
+
+ for (NetworkState state : states) {
+ if (state.networkInfo.isConnected()) {
+ // collect networks under their parent interfaces
+ final String iface = state.linkProperties.getInterfaceName();
+ final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface);
+ ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
+ }
+ }
+ }
+
private void performPollLocked() {
if (LOGD) Slog.v(TAG, "performPollLocked()");
@@ -258,13 +307,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return;
}
+ final ArrayList<String> unknownIface = Lists.newArrayList();
+
// update historical usage with delta since last poll
final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current);
final long timeStart = currentTime - pollDelta.elapsedRealtime;
for (String iface : pollDelta.getKnownIfaces()) {
- final InterfaceInfo info = mActiveIface.get(iface);
- if (info == null) {
- if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats");
+ final InterfaceIdentity ident = mActiveIface.get(iface);
+ if (ident == null) {
+ unknownIface.add(iface);
continue;
}
@@ -272,11 +323,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long rx = pollDelta.rx[index];
final long tx = pollDelta.tx[index];
- final NetworkStatsHistory history = findOrCreateHistoryLocked(info);
+ final NetworkStatsHistory history = findOrCreateHistoryLocked(ident);
history.recordData(timeStart, currentTime, rx, tx);
history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY);
}
+ if (LOGD && unknownIface.size() > 0) {
+ Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
+ }
+
mLastPollStats = current;
// decide if enough has changed to trigger persist
@@ -292,16 +347,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) {
- NetworkStatsHistory stats = mIfaceStats.get(info);
+ private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceIdentity ident) {
+ NetworkStatsHistory stats = mStats.get(ident);
if (stats == null) {
- stats = new NetworkStatsHistory(
- info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION);
- mIfaceStats.put(info, stats);
+ stats = new NetworkStatsHistory(SUMMARY_BUCKET_DURATION);
+ mStats.put(ident, stats);
}
return stats;
}
+ private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
+ InterfaceIdentity ident = mActiveIface.get(iface);
+ if (ident == null) {
+ ident = new InterfaceIdentity();
+ mActiveIface.put(iface, ident);
+ }
+ return ident;
+ }
+
private void readStatsLocked() {
if (LOGD) Slog.v(TAG, "readStatsLocked()");
// TODO: read historical stats from disk using AtomicFile
@@ -310,6 +373,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private void writeStatsLocked() {
if (LOGD) Slog.v(TAG, "writeStatsLocked()");
// TODO: persist historical stats to disk using AtomicFile
+ // TODO: consider duplicating stats and releasing lock while writing
}
@Override
@@ -317,63 +381,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
pw.println("Active interfaces:");
- for (InterfaceInfo info : mActiveIface.values()) {
- info.dump(" ", pw);
+ for (String iface : mActiveIface.keySet()) {
+ final InterfaceIdentity ident = mActiveIface.get(iface);
+ pw.print(" iface="); pw.print(iface);
+ pw.print(" ident="); pw.println(ident.toString());
}
pw.println("Known historical stats:");
- for (NetworkStatsHistory stats : mIfaceStats.values()) {
- stats.dump(" ", pw);
- }
- }
-
- /**
- * Details for a well-known network interface, including its name, network
- * type, and billing relationship identity (such as IMSI).
- */
- private static class InterfaceInfo {
- public final String iface;
- public final int networkType;
- public final String identity;
-
- public InterfaceInfo(String iface, int networkType, String identity) {
- this.iface = iface;
- this.networkType = networkType;
- this.identity = identity;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((identity == null) ? 0 : identity.hashCode());
- result = prime * result + ((iface == null) ? 0 : iface.hashCode());
- result = prime * result + networkType;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof InterfaceInfo) {
- final InterfaceInfo info = (InterfaceInfo) obj;
- return equal(iface, info.iface) && networkType == info.networkType
- && equal(identity, info.identity);
- }
- return false;
- }
-
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix);
- pw.print("InterfaceInfo: iface="); pw.print(iface);
- pw.print(" networkType="); pw.print(networkType);
- pw.print(" identity="); pw.println(identity);
- }
- }
-
- private void reportActiveInterface(InterfaceInfo info) {
- synchronized (mStatsLock) {
- // TODO: when interface redefined, port over historical stats
- mActiveIface.put(info.iface, info);
+ for (InterfaceIdentity ident : mStats.keySet()) {
+ final NetworkStatsHistory stats = mStats.get(ident);
+ pw.print(" ident="); pw.println(ident.toString());
+ stats.dump(" ", pw);
}
}
@@ -389,15 +407,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- private static boolean equal(Object a, Object b) {
- return a == b || (a != null && a.equals(b));
- }
-
- private static <T> T checkNotNull(T value, String message) {
- if (value == null) {
- throw new NullPointerException(message);
- }
- return value;
+ private String getActiveSubscriberId() {
+ final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephony.getSubscriberId();
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8732e21..7272b08 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -469,6 +469,46 @@ public class TelephonyManager {
}
}
+ /** Unknown network class. {@hide} */
+ public static final int NETWORK_CLASS_UNKNOWN = 0;
+ /** Class of broadly defined "2G" networks. {@hide} */
+ public static final int NETWORK_CLASS_2_G = 1;
+ /** Class of broadly defined "3G" networks. {@hide} */
+ public static final int NETWORK_CLASS_3_G = 2;
+ /** Class of broadly defined "4G" networks. {@hide} */
+ public static final int NETWORK_CLASS_4_G = 3;
+
+ /**
+ * Return general class of network type, such as "3G" or "4G". In cases
+ * where classification is contentious, this method is conservative.
+ *
+ * @hide
+ */
+ public static int getNetworkClass(int networkType) {
+ switch (networkType) {
+ case NETWORK_TYPE_GPRS:
+ case NETWORK_TYPE_EDGE:
+ case NETWORK_TYPE_CDMA:
+ case NETWORK_TYPE_1xRTT:
+ case NETWORK_TYPE_IDEN:
+ return NETWORK_CLASS_2_G;
+ case NETWORK_TYPE_UMTS:
+ case NETWORK_TYPE_EVDO_0:
+ case NETWORK_TYPE_EVDO_A:
+ case NETWORK_TYPE_HSDPA:
+ case NETWORK_TYPE_HSUPA:
+ case NETWORK_TYPE_HSPA:
+ case NETWORK_TYPE_EVDO_B:
+ case NETWORK_TYPE_EHRPD:
+ case NETWORK_TYPE_HSPAP:
+ return NETWORK_CLASS_3_G;
+ case NETWORK_TYPE_LTE:
+ return NETWORK_CLASS_4_G;
+ default:
+ return NETWORK_CLASS_UNKNOWN;
+ }
+ }
+
/**
* Returns a string representation of the radio technology (network type)
* currently in use on the device.
@@ -477,7 +517,12 @@ public class TelephonyManager {
* @hide pending API council review
*/
public String getNetworkTypeName() {
- switch (getNetworkType()) {
+ return getNetworkTypeName(getNetworkType());
+ }
+
+ /** {@hide} */
+ public static String getNetworkTypeName(int type) {
+ switch (type) {
case NETWORK_TYPE_GPRS:
return "GPRS";
case NETWORK_TYPE_EDGE: