diff options
author | Jeff Sharkey <jsharkey@android.com> | 2011-05-28 20:56:34 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2011-06-07 23:09:25 -0700 |
commit | d2a458750e5a3d490af09cecb5c28370baf0a913 (patch) | |
tree | 72881850dcc70e3b74c52be52ec4858c96a98a8e | |
parent | dd82b85677b3556776dbf023ad4fdc22cf075523 (diff) | |
download | frameworks_base-d2a458750e5a3d490af09cecb5c28370baf0a913.zip frameworks_base-d2a458750e5a3d490af09cecb5c28370baf0a913.tar.gz frameworks_base-d2a458750e5a3d490af09cecb5c28370baf0a913.tar.bz2 |
Map network identity using ConnectivityService.
Instead of deriving network identity based on raw subsystem broadcasts,
listen for updates from ConnectivityService. Added atomic view of all
active NetworkState, and build map from "iface" to NetworkIdentity set
for stats tracking.
To avoid exposing internal complexity, INetworkStatsService calls use
general templates. Added TelephonyManager mapping to classify network
types using broad labels like "3G" or "4G", used to drive templates.
Cleaned up Objects and Preconditions.
Change-Id: I1d4c1403f0503bc3635a59bb378841ba42239a91
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: |