NetworkTemplate.java revision 3256601f5e4d94713f59e97b9d4912875c1bdcaf
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.net; 18 19import static android.net.ConnectivityManager.TYPE_BLUETOOTH; 20import static android.net.ConnectivityManager.TYPE_ETHERNET; 21import static android.net.ConnectivityManager.TYPE_WIFI; 22import static android.net.ConnectivityManager.TYPE_WIFI_P2P; 23import static android.net.ConnectivityManager.TYPE_WIMAX; 24import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; 25import static android.net.wifi.WifiInfo.removeDoubleQuotes; 26import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; 27import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; 28import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; 29import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; 30import static android.telephony.TelephonyManager.getNetworkClass; 31import static com.android.internal.util.ArrayUtils.contains; 32 33import android.content.res.Resources; 34import android.os.Parcel; 35import android.os.Parcelable; 36 37import com.android.internal.annotations.VisibleForTesting; 38import com.android.internal.util.ArrayUtils; 39 40import java.util.Arrays; 41import java.util.Objects; 42 43/** 44 * Template definition used to generically match {@link NetworkIdentity}, 45 * usually when collecting statistics. 46 * 47 * @hide 48 */ 49public class NetworkTemplate implements Parcelable { 50 51 public static final int MATCH_MOBILE_ALL = 1; 52 public static final int MATCH_MOBILE_3G_LOWER = 2; 53 public static final int MATCH_MOBILE_4G = 3; 54 public static final int MATCH_WIFI = 4; 55 public static final int MATCH_ETHERNET = 5; 56 public static final int MATCH_MOBILE_WILDCARD = 6; 57 public static final int MATCH_WIFI_WILDCARD = 7; 58 public static final int MATCH_BLUETOOTH = 8; 59 60 /** 61 * Set of {@link NetworkInfo#getType()} that reflect data usage. 62 */ 63 private static final int[] DATA_USAGE_NETWORK_TYPES; 64 65 static { 66 DATA_USAGE_NETWORK_TYPES = Resources.getSystem().getIntArray( 67 com.android.internal.R.array.config_data_usage_network_types); 68 } 69 70 private static boolean sForceAllNetworkTypes = false; 71 72 @VisibleForTesting 73 public static void forceAllNetworkTypes() { 74 sForceAllNetworkTypes = true; 75 } 76 77 /** 78 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 79 * the given IMSI. 80 */ 81 public static NetworkTemplate buildTemplateMobileAll(String subscriberId) { 82 return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null); 83 } 84 85 /** 86 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 87 * the given IMSI that roughly meet a "3G" definition, or lower. 88 */ 89 @Deprecated 90 public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) { 91 return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null); 92 } 93 94 /** 95 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 96 * the given IMSI that roughly meet a "4G" definition. 97 */ 98 @Deprecated 99 public static NetworkTemplate buildTemplateMobile4g(String subscriberId) { 100 return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null); 101 } 102 103 /** 104 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks, 105 * regardless of IMSI. 106 */ 107 public static NetworkTemplate buildTemplateMobileWildcard() { 108 return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null); 109 } 110 111 /** 112 * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks, 113 * regardless of SSID. 114 */ 115 public static NetworkTemplate buildTemplateWifiWildcard() { 116 return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null); 117 } 118 119 @Deprecated 120 public static NetworkTemplate buildTemplateWifi() { 121 return buildTemplateWifiWildcard(); 122 } 123 124 /** 125 * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the 126 * given SSID. 127 */ 128 public static NetworkTemplate buildTemplateWifi(String networkId) { 129 return new NetworkTemplate(MATCH_WIFI, null, networkId); 130 } 131 132 /** 133 * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style 134 * networks together. 135 */ 136 public static NetworkTemplate buildTemplateEthernet() { 137 return new NetworkTemplate(MATCH_ETHERNET, null, null); 138 } 139 140 /** 141 * Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style 142 * networks together. 143 */ 144 public static NetworkTemplate buildTemplateBluetooth() { 145 return new NetworkTemplate(MATCH_BLUETOOTH, null, null); 146 } 147 148 private final int mMatchRule; 149 private final String mSubscriberId; 150 151 /** 152 * Ugh, templates are designed to target a single subscriber, but we might 153 * need to match several "merged" subscribers. These are the subscribers 154 * that should be considered to match this template. 155 * <p> 156 * Since the merge set is dynamic, it should <em>not</em> be persisted or 157 * used for determining equality. 158 */ 159 private final String[] mMatchSubscriberIds; 160 161 private final String mNetworkId; 162 163 public NetworkTemplate(int matchRule, String subscriberId, String networkId) { 164 this(matchRule, subscriberId, new String[] { subscriberId }, networkId); 165 } 166 167 public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, 168 String networkId) { 169 mMatchRule = matchRule; 170 mSubscriberId = subscriberId; 171 mMatchSubscriberIds = matchSubscriberIds; 172 mNetworkId = networkId; 173 } 174 175 private NetworkTemplate(Parcel in) { 176 mMatchRule = in.readInt(); 177 mSubscriberId = in.readString(); 178 mMatchSubscriberIds = in.createStringArray(); 179 mNetworkId = in.readString(); 180 } 181 182 @Override 183 public void writeToParcel(Parcel dest, int flags) { 184 dest.writeInt(mMatchRule); 185 dest.writeString(mSubscriberId); 186 dest.writeStringArray(mMatchSubscriberIds); 187 dest.writeString(mNetworkId); 188 } 189 190 @Override 191 public int describeContents() { 192 return 0; 193 } 194 195 @Override 196 public String toString() { 197 final StringBuilder builder = new StringBuilder("NetworkTemplate: "); 198 builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); 199 if (mSubscriberId != null) { 200 builder.append(", subscriberId=").append( 201 NetworkIdentity.scrubSubscriberId(mSubscriberId)); 202 } 203 if (mMatchSubscriberIds != null) { 204 builder.append(", matchSubscriberIds=").append( 205 Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds))); 206 } 207 if (mNetworkId != null) { 208 builder.append(", networkId=").append(mNetworkId); 209 } 210 return builder.toString(); 211 } 212 213 @Override 214 public int hashCode() { 215 return Objects.hash(mMatchRule, mSubscriberId, mNetworkId); 216 } 217 218 @Override 219 public boolean equals(Object obj) { 220 if (obj instanceof NetworkTemplate) { 221 final NetworkTemplate other = (NetworkTemplate) obj; 222 return mMatchRule == other.mMatchRule 223 && Objects.equals(mSubscriberId, other.mSubscriberId) 224 && Objects.equals(mNetworkId, other.mNetworkId); 225 } 226 return false; 227 } 228 229 public boolean isMatchRuleMobile() { 230 switch (mMatchRule) { 231 case MATCH_MOBILE_3G_LOWER: 232 case MATCH_MOBILE_4G: 233 case MATCH_MOBILE_ALL: 234 case MATCH_MOBILE_WILDCARD: 235 return true; 236 default: 237 return false; 238 } 239 } 240 241 public int getMatchRule() { 242 return mMatchRule; 243 } 244 245 public String getSubscriberId() { 246 return mSubscriberId; 247 } 248 249 public String getNetworkId() { 250 return mNetworkId; 251 } 252 253 /** 254 * Test if given {@link NetworkIdentity} matches this template. 255 */ 256 public boolean matches(NetworkIdentity ident) { 257 switch (mMatchRule) { 258 case MATCH_MOBILE_ALL: 259 return matchesMobile(ident); 260 case MATCH_MOBILE_3G_LOWER: 261 return matchesMobile3gLower(ident); 262 case MATCH_MOBILE_4G: 263 return matchesMobile4g(ident); 264 case MATCH_WIFI: 265 return matchesWifi(ident); 266 case MATCH_ETHERNET: 267 return matchesEthernet(ident); 268 case MATCH_MOBILE_WILDCARD: 269 return matchesMobileWildcard(ident); 270 case MATCH_WIFI_WILDCARD: 271 return matchesWifiWildcard(ident); 272 case MATCH_BLUETOOTH: 273 return matchesBluetooth(ident); 274 default: 275 throw new IllegalArgumentException("unknown network template"); 276 } 277 } 278 279 /** 280 * Check if mobile network with matching IMSI. 281 */ 282 private boolean matchesMobile(NetworkIdentity ident) { 283 if (ident.mType == TYPE_WIMAX) { 284 // TODO: consider matching against WiMAX subscriber identity 285 return true; 286 } else { 287 final boolean matchesType = (sForceAllNetworkTypes 288 || contains(DATA_USAGE_NETWORK_TYPES, ident.mType)); 289 return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); 290 } 291 } 292 293 /** 294 * Check if mobile network classified 3G or lower with matching IMSI. 295 */ 296 private boolean matchesMobile3gLower(NetworkIdentity ident) { 297 ensureSubtypeAvailable(); 298 if (ident.mType == TYPE_WIMAX) { 299 return false; 300 } else if (matchesMobile(ident)) { 301 switch (getNetworkClass(ident.mSubType)) { 302 case NETWORK_CLASS_UNKNOWN: 303 case NETWORK_CLASS_2_G: 304 case NETWORK_CLASS_3_G: 305 return true; 306 } 307 } 308 return false; 309 } 310 311 /** 312 * Check if mobile network classified 4G with matching IMSI. 313 */ 314 private boolean matchesMobile4g(NetworkIdentity ident) { 315 ensureSubtypeAvailable(); 316 if (ident.mType == TYPE_WIMAX) { 317 // TODO: consider matching against WiMAX subscriber identity 318 return true; 319 } else if (matchesMobile(ident)) { 320 switch (getNetworkClass(ident.mSubType)) { 321 case NETWORK_CLASS_4_G: 322 return true; 323 } 324 } 325 return false; 326 } 327 328 /** 329 * Check if matches Wi-Fi network template. 330 */ 331 private boolean matchesWifi(NetworkIdentity ident) { 332 switch (ident.mType) { 333 case TYPE_WIFI: 334 return Objects.equals( 335 removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId)); 336 default: 337 return false; 338 } 339 } 340 341 /** 342 * Check if matches Ethernet network template. 343 */ 344 private boolean matchesEthernet(NetworkIdentity ident) { 345 if (ident.mType == TYPE_ETHERNET) { 346 return true; 347 } 348 return false; 349 } 350 351 private boolean matchesMobileWildcard(NetworkIdentity ident) { 352 if (ident.mType == TYPE_WIMAX) { 353 return true; 354 } else { 355 return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType); 356 } 357 } 358 359 private boolean matchesWifiWildcard(NetworkIdentity ident) { 360 switch (ident.mType) { 361 case TYPE_WIFI: 362 case TYPE_WIFI_P2P: 363 return true; 364 default: 365 return false; 366 } 367 } 368 369 /** 370 * Check if matches Bluetooth network template. 371 */ 372 private boolean matchesBluetooth(NetworkIdentity ident) { 373 if (ident.mType == TYPE_BLUETOOTH) { 374 return true; 375 } 376 return false; 377 } 378 379 private static String getMatchRuleName(int matchRule) { 380 switch (matchRule) { 381 case MATCH_MOBILE_3G_LOWER: 382 return "MOBILE_3G_LOWER"; 383 case MATCH_MOBILE_4G: 384 return "MOBILE_4G"; 385 case MATCH_MOBILE_ALL: 386 return "MOBILE_ALL"; 387 case MATCH_WIFI: 388 return "WIFI"; 389 case MATCH_ETHERNET: 390 return "ETHERNET"; 391 case MATCH_MOBILE_WILDCARD: 392 return "MOBILE_WILDCARD"; 393 case MATCH_WIFI_WILDCARD: 394 return "WIFI_WILDCARD"; 395 case MATCH_BLUETOOTH: 396 return "BLUETOOTH"; 397 default: 398 return "UNKNOWN"; 399 } 400 } 401 402 private static void ensureSubtypeAvailable() { 403 if (COMBINE_SUBTYPE_ENABLED) { 404 throw new IllegalArgumentException( 405 "Unable to enforce 3G_LOWER template on combined data."); 406 } 407 } 408 409 /** 410 * Examine the given template and normalize if it refers to a "merged" 411 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 412 * for key purposes, and expand the template to match all other merged 413 * subscribers. 414 * <p> 415 * For example, given an incoming template matching B, and the currently 416 * active merge set [A,B], we'd return a new template that primarily matches 417 * A, but also matches B. 418 */ 419 public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { 420 if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { 421 // Requested template subscriber is part of the merge group; return 422 // a template that matches all merged subscribers. 423 return new NetworkTemplate(template.mMatchRule, merged[0], merged, 424 template.mNetworkId); 425 } else { 426 return template; 427 } 428 } 429 430 public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { 431 @Override 432 public NetworkTemplate createFromParcel(Parcel in) { 433 return new NetworkTemplate(in); 434 } 435 436 @Override 437 public NetworkTemplate[] newArray(int size) { 438 return new NetworkTemplate[size]; 439 } 440 }; 441} 442