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
This commit is contained in:
Jeff Sharkey
2011-05-28 20:56:34 -07:00
parent ab2c05370e
commit 21062e7c7e
5 changed files with 174 additions and 43 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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];
}
};
}

View File

@@ -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++) {

View File

@@ -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();