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