Offer to "merge" subscribers for data usage.
There are some cases where multiple subscriber identities (IMSI) should be treated as "merged together" from a data usage perspective. This is done by extending the template used for matching purposes to support multiple subscribers. Then, when we query historical usage or set network policies, we normalize the matching template to merge to any other identities that should be included. When normalizing, the "lowest" identity is always used for equality and storage purposes, which allows identities to come and go over time. This change also fixes data usage recording for multi-SIM devices by passing along the concrete subscriber identity for each network interface. Also correctly create default policies for multi-SIM devices. This change also drops setPolicyDataEnable() until it can be wired up to the right underlying NetworkAgent. (This means we still bring up the network, and then rely on iptables rules to block traffic when over the limit, instead of proactively disabling the connection.) Bug: 18012787 Change-Id: If6acf32009fdfea2b836f5aff8e2f3e5e0248b4a
This commit is contained in:
@@ -25,6 +25,7 @@ import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -35,6 +36,8 @@ import java.util.Objects;
|
||||
* @hide
|
||||
*/
|
||||
public class NetworkIdentity implements Comparable<NetworkIdentity> {
|
||||
private static final String TAG = "NetworkIdentity";
|
||||
|
||||
/**
|
||||
* When enabled, combine all {@link #mSubType} together under
|
||||
* {@link #SUBTYPE_COMBINED}.
|
||||
@@ -132,6 +135,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrub given IMSI on production builds.
|
||||
*/
|
||||
public static String[] scrubSubscriberId(String[] subscriberId) {
|
||||
if (subscriberId == null) return null;
|
||||
final String[] res = new String[subscriberId.length];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link NetworkIdentity} from the given {@link NetworkState},
|
||||
* assuming that any mobile networks are using the current IMSI.
|
||||
@@ -140,23 +155,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
|
||||
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.
|
||||
|
||||
String subscriberId = null;
|
||||
String networkId = null;
|
||||
boolean roaming = false;
|
||||
|
||||
if (isNetworkTypeMobile(type)) {
|
||||
final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
|
||||
Context.TELEPHONY_SERVICE);
|
||||
roaming = telephony.isNetworkRoaming();
|
||||
if (state.subscriberId != null) {
|
||||
subscriberId = state.subscriberId;
|
||||
} else {
|
||||
subscriberId = telephony.getSubscriberId();
|
||||
if (state.subscriberId == null) {
|
||||
Slog.w(TAG, "Active mobile network without subscriber!");
|
||||
}
|
||||
|
||||
subscriberId = state.subscriberId;
|
||||
roaming = state.networkInfo.isRoaming();
|
||||
|
||||
} else if (type == TYPE_WIFI) {
|
||||
if (state.networkId != null) {
|
||||
networkId = state.networkId;
|
||||
|
||||
@@ -22,7 +22,6 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
|
||||
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
|
||||
import static android.net.ConnectivityManager.TYPE_WIMAX;
|
||||
import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
|
||||
import static android.net.NetworkIdentity.scrubSubscriberId;
|
||||
import static android.net.wifi.WifiInfo.removeDoubleQuotes;
|
||||
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
|
||||
import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
|
||||
@@ -36,7 +35,9 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -146,17 +147,35 @@ public class NetworkTemplate implements Parcelable {
|
||||
|
||||
private final int mMatchRule;
|
||||
private final String mSubscriberId;
|
||||
|
||||
/**
|
||||
* Ugh, templates are designed to target a single subscriber, but we might
|
||||
* need to match several "merged" subscribers. These are the subscribers
|
||||
* that should be considered to match this template.
|
||||
* <p>
|
||||
* Since the merge set is dynamic, it should <em>not</em> be persisted or
|
||||
* used for determining equality.
|
||||
*/
|
||||
private final String[] mMatchSubscriberIds;
|
||||
|
||||
private final String mNetworkId;
|
||||
|
||||
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
|
||||
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
|
||||
}
|
||||
|
||||
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
|
||||
String networkId) {
|
||||
mMatchRule = matchRule;
|
||||
mSubscriberId = subscriberId;
|
||||
mMatchSubscriberIds = matchSubscriberIds;
|
||||
mNetworkId = networkId;
|
||||
}
|
||||
|
||||
private NetworkTemplate(Parcel in) {
|
||||
mMatchRule = in.readInt();
|
||||
mSubscriberId = in.readString();
|
||||
mMatchSubscriberIds = in.createStringArray();
|
||||
mNetworkId = in.readString();
|
||||
}
|
||||
|
||||
@@ -164,6 +183,7 @@ public class NetworkTemplate implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mMatchRule);
|
||||
dest.writeString(mSubscriberId);
|
||||
dest.writeStringArray(mMatchSubscriberIds);
|
||||
dest.writeString(mNetworkId);
|
||||
}
|
||||
|
||||
@@ -177,7 +197,12 @@ public class NetworkTemplate implements Parcelable {
|
||||
final StringBuilder builder = new StringBuilder("NetworkTemplate: ");
|
||||
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
|
||||
if (mSubscriberId != null) {
|
||||
builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
|
||||
builder.append(", subscriberId=").append(
|
||||
NetworkIdentity.scrubSubscriberId(mSubscriberId));
|
||||
}
|
||||
if (mMatchSubscriberIds != null) {
|
||||
builder.append(", matchSubscriberIds=").append(
|
||||
Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
|
||||
}
|
||||
if (mNetworkId != null) {
|
||||
builder.append(", networkId=").append(mNetworkId);
|
||||
@@ -201,6 +226,18 @@ public class NetworkTemplate implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMatchRuleMobile() {
|
||||
switch (mMatchRule) {
|
||||
case MATCH_MOBILE_3G_LOWER:
|
||||
case MATCH_MOBILE_4G:
|
||||
case MATCH_MOBILE_ALL:
|
||||
case MATCH_MOBILE_WILDCARD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMatchRule() {
|
||||
return mMatchRule;
|
||||
}
|
||||
@@ -247,8 +284,9 @@ public class NetworkTemplate implements Parcelable {
|
||||
// TODO: consider matching against WiMAX subscriber identity
|
||||
return true;
|
||||
} else {
|
||||
return ((sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType))
|
||||
&& Objects.equals(mSubscriberId, ident.mSubscriberId));
|
||||
final boolean matchesType = (sForceAllNetworkTypes
|
||||
|| contains(DATA_USAGE_NETWORK_TYPES, ident.mType));
|
||||
return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +406,27 @@ public class NetworkTemplate implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the given template and normalize if it refers to a "merged"
|
||||
* mobile subscriber. We pick the "lowest" merged subscriber as the primary
|
||||
* for key purposes, and expand the template to match all other merged
|
||||
* subscribers.
|
||||
* <p>
|
||||
* For example, given an incoming template matching B, and the currently
|
||||
* active merge set [A,B], we'd return a new template that primarily matches
|
||||
* A, but also matches B.
|
||||
*/
|
||||
public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
|
||||
if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) {
|
||||
// Requested template subscriber is part of the merge group; return
|
||||
// a template that matches all merged subscribers.
|
||||
return new NetworkTemplate(template.mMatchRule, merged[0], merged,
|
||||
template.mNetworkId);
|
||||
} else {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
|
||||
@Override
|
||||
public NetworkTemplate createFromParcel(Parcel in) {
|
||||
|
||||
Reference in New Issue
Block a user