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 17 package android.net; 18 19 import static android.net.ConnectivityManager.TYPE_BLUETOOTH; 20 import static android.net.ConnectivityManager.TYPE_ETHERNET; 21 import static android.net.ConnectivityManager.TYPE_MOBILE; 22 import static android.net.ConnectivityManager.TYPE_PROXY; 23 import static android.net.ConnectivityManager.TYPE_WIFI; 24 import static android.net.ConnectivityManager.TYPE_WIFI_P2P; 25 import static android.net.ConnectivityManager.TYPE_WIMAX; 26 import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; 27 import static android.net.wifi.WifiInfo.removeDoubleQuotes; 28 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; 29 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; 30 import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; 31 import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; 32 import static android.telephony.TelephonyManager.getNetworkClass; 33 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 import android.util.BackupUtils; 37 import android.util.Log; 38 39 import com.android.internal.util.ArrayUtils; 40 41 import java.io.ByteArrayOutputStream; 42 import java.io.DataInputStream; 43 import java.io.DataOutputStream; 44 import java.io.IOException; 45 import java.util.Arrays; 46 import java.util.Objects; 47 48 /** 49 * Template definition used to generically match {@link NetworkIdentity}, 50 * usually when collecting statistics. 51 * 52 * @hide 53 */ 54 public class NetworkTemplate implements Parcelable { 55 private static final String TAG = "NetworkTemplate"; 56 57 /** 58 * Current Version of the Backup Serializer. 59 */ 60 private static final int BACKUP_VERSION = 1; 61 62 public static final int MATCH_MOBILE_ALL = 1; 63 /** @deprecated don't use this any more */ 64 @Deprecated 65 public static final int MATCH_MOBILE_3G_LOWER = 2; 66 /** @deprecated don't use this any more */ 67 @Deprecated 68 public static final int MATCH_MOBILE_4G = 3; 69 public static final int MATCH_WIFI = 4; 70 public static final int MATCH_ETHERNET = 5; 71 public static final int MATCH_MOBILE_WILDCARD = 6; 72 public static final int MATCH_WIFI_WILDCARD = 7; 73 public static final int MATCH_BLUETOOTH = 8; 74 public static final int MATCH_PROXY = 9; 75 76 private static boolean isKnownMatchRule(final int rule) { 77 switch (rule) { 78 case MATCH_MOBILE_ALL: 79 case MATCH_MOBILE_3G_LOWER: 80 case MATCH_MOBILE_4G: 81 case MATCH_WIFI: 82 case MATCH_ETHERNET: 83 case MATCH_MOBILE_WILDCARD: 84 case MATCH_WIFI_WILDCARD: 85 case MATCH_BLUETOOTH: 86 case MATCH_PROXY: 87 return true; 88 89 default: 90 return false; 91 } 92 } 93 94 private static boolean sForceAllNetworkTypes = false; 95 96 public static void forceAllNetworkTypes() { 97 sForceAllNetworkTypes = true; 98 } 99 100 /** 101 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 102 * the given IMSI. 103 */ 104 public static NetworkTemplate buildTemplateMobileAll(String subscriberId) { 105 return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null); 106 } 107 108 /** 109 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 110 * the given IMSI that roughly meet a "3G" definition, or lower. 111 */ 112 @Deprecated 113 public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) { 114 return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null); 115 } 116 117 /** 118 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 119 * the given IMSI that roughly meet a "4G" definition. 120 */ 121 @Deprecated 122 public static NetworkTemplate buildTemplateMobile4g(String subscriberId) { 123 return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null); 124 } 125 126 /** 127 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks, 128 * regardless of IMSI. 129 */ 130 public static NetworkTemplate buildTemplateMobileWildcard() { 131 return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null); 132 } 133 134 /** 135 * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks, 136 * regardless of SSID. 137 */ 138 public static NetworkTemplate buildTemplateWifiWildcard() { 139 return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null); 140 } 141 142 @Deprecated 143 public static NetworkTemplate buildTemplateWifi() { 144 return buildTemplateWifiWildcard(); 145 } 146 147 /** 148 * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the 149 * given SSID. 150 */ 151 public static NetworkTemplate buildTemplateWifi(String networkId) { 152 return new NetworkTemplate(MATCH_WIFI, null, networkId); 153 } 154 155 /** 156 * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style 157 * networks together. 158 */ 159 public static NetworkTemplate buildTemplateEthernet() { 160 return new NetworkTemplate(MATCH_ETHERNET, null, null); 161 } 162 163 /** 164 * Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style 165 * networks together. 166 */ 167 public static NetworkTemplate buildTemplateBluetooth() { 168 return new NetworkTemplate(MATCH_BLUETOOTH, null, null); 169 } 170 171 /** 172 * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style 173 * networks together. 174 */ 175 public static NetworkTemplate buildTemplateProxy() { 176 return new NetworkTemplate(MATCH_PROXY, null, null); 177 } 178 179 private final int mMatchRule; 180 private final String mSubscriberId; 181 182 /** 183 * Ugh, templates are designed to target a single subscriber, but we might 184 * need to match several "merged" subscribers. These are the subscribers 185 * that should be considered to match this template. 186 * <p> 187 * Since the merge set is dynamic, it should <em>not</em> be persisted or 188 * used for determining equality. 189 */ 190 private final String[] mMatchSubscriberIds; 191 192 private final String mNetworkId; 193 194 public NetworkTemplate(int matchRule, String subscriberId, String networkId) { 195 this(matchRule, subscriberId, new String[] { subscriberId }, networkId); 196 } 197 198 public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, 199 String networkId) { 200 mMatchRule = matchRule; 201 mSubscriberId = subscriberId; 202 mMatchSubscriberIds = matchSubscriberIds; 203 mNetworkId = networkId; 204 205 if (!isKnownMatchRule(matchRule)) { 206 Log.e(TAG, "Unknown network template rule " + matchRule 207 + " will not match any identity."); 208 } 209 } 210 211 private NetworkTemplate(Parcel in) { 212 mMatchRule = in.readInt(); 213 mSubscriberId = in.readString(); 214 mMatchSubscriberIds = in.createStringArray(); 215 mNetworkId = in.readString(); 216 } 217 218 @Override 219 public void writeToParcel(Parcel dest, int flags) { 220 dest.writeInt(mMatchRule); 221 dest.writeString(mSubscriberId); 222 dest.writeStringArray(mMatchSubscriberIds); 223 dest.writeString(mNetworkId); 224 } 225 226 @Override 227 public int describeContents() { 228 return 0; 229 } 230 231 @Override 232 public String toString() { 233 final StringBuilder builder = new StringBuilder("NetworkTemplate: "); 234 builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); 235 if (mSubscriberId != null) { 236 builder.append(", subscriberId=").append( 237 NetworkIdentity.scrubSubscriberId(mSubscriberId)); 238 } 239 if (mMatchSubscriberIds != null) { 240 builder.append(", matchSubscriberIds=").append( 241 Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds))); 242 } 243 if (mNetworkId != null) { 244 builder.append(", networkId=").append(mNetworkId); 245 } 246 return builder.toString(); 247 } 248 249 @Override 250 public int hashCode() { 251 return Objects.hash(mMatchRule, mSubscriberId, mNetworkId); 252 } 253 254 @Override 255 public boolean equals(Object obj) { 256 if (obj instanceof NetworkTemplate) { 257 final NetworkTemplate other = (NetworkTemplate) obj; 258 return mMatchRule == other.mMatchRule 259 && Objects.equals(mSubscriberId, other.mSubscriberId) 260 && Objects.equals(mNetworkId, other.mNetworkId); 261 } 262 return false; 263 } 264 265 public boolean isMatchRuleMobile() { 266 switch (mMatchRule) { 267 case MATCH_MOBILE_3G_LOWER: 268 case MATCH_MOBILE_4G: 269 case MATCH_MOBILE_ALL: 270 case MATCH_MOBILE_WILDCARD: 271 return true; 272 default: 273 return false; 274 } 275 } 276 277 public boolean isPersistable() { 278 switch (mMatchRule) { 279 case MATCH_MOBILE_WILDCARD: 280 case MATCH_WIFI_WILDCARD: 281 return false; 282 default: 283 return true; 284 } 285 } 286 287 public int getMatchRule() { 288 return mMatchRule; 289 } 290 291 public String getSubscriberId() { 292 return mSubscriberId; 293 } 294 295 public String getNetworkId() { 296 return mNetworkId; 297 } 298 299 /** 300 * Test if given {@link NetworkIdentity} matches this template. 301 */ 302 public boolean matches(NetworkIdentity ident) { 303 switch (mMatchRule) { 304 case MATCH_MOBILE_ALL: 305 return matchesMobile(ident); 306 case MATCH_MOBILE_3G_LOWER: 307 return matchesMobile3gLower(ident); 308 case MATCH_MOBILE_4G: 309 return matchesMobile4g(ident); 310 case MATCH_WIFI: 311 return matchesWifi(ident); 312 case MATCH_ETHERNET: 313 return matchesEthernet(ident); 314 case MATCH_MOBILE_WILDCARD: 315 return matchesMobileWildcard(ident); 316 case MATCH_WIFI_WILDCARD: 317 return matchesWifiWildcard(ident); 318 case MATCH_BLUETOOTH: 319 return matchesBluetooth(ident); 320 case MATCH_PROXY: 321 return matchesProxy(ident); 322 default: 323 // We have no idea what kind of network template we are, so we 324 // just claim not to match anything. 325 return false; 326 } 327 } 328 329 public boolean matchesSubscriberId(String subscriberId) { 330 return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); 331 } 332 333 /** 334 * Check if mobile network with matching IMSI. 335 */ 336 private boolean matchesMobile(NetworkIdentity ident) { 337 if (ident.mType == TYPE_WIMAX) { 338 // TODO: consider matching against WiMAX subscriber identity 339 return true; 340 } else { 341 return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) 342 && !ArrayUtils.isEmpty(mMatchSubscriberIds) 343 && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); 344 } 345 } 346 347 /** 348 * Check if mobile network classified 3G or lower with matching IMSI. 349 */ 350 @Deprecated 351 private boolean matchesMobile3gLower(NetworkIdentity ident) { 352 ensureSubtypeAvailable(); 353 if (ident.mType == TYPE_WIMAX) { 354 return false; 355 } else if (matchesMobile(ident)) { 356 switch (getNetworkClass(ident.mSubType)) { 357 case NETWORK_CLASS_UNKNOWN: 358 case NETWORK_CLASS_2_G: 359 case NETWORK_CLASS_3_G: 360 return true; 361 } 362 } 363 return false; 364 } 365 366 /** 367 * Check if mobile network classified 4G with matching IMSI. 368 */ 369 @Deprecated 370 private boolean matchesMobile4g(NetworkIdentity ident) { 371 ensureSubtypeAvailable(); 372 if (ident.mType == TYPE_WIMAX) { 373 // TODO: consider matching against WiMAX subscriber identity 374 return true; 375 } else if (matchesMobile(ident)) { 376 switch (getNetworkClass(ident.mSubType)) { 377 case NETWORK_CLASS_4_G: 378 return true; 379 } 380 } 381 return false; 382 } 383 384 /** 385 * Check if matches Wi-Fi network template. 386 */ 387 private boolean matchesWifi(NetworkIdentity ident) { 388 switch (ident.mType) { 389 case TYPE_WIFI: 390 return Objects.equals( 391 removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId)); 392 default: 393 return false; 394 } 395 } 396 397 /** 398 * Check if matches Ethernet network template. 399 */ 400 private boolean matchesEthernet(NetworkIdentity ident) { 401 if (ident.mType == TYPE_ETHERNET) { 402 return true; 403 } 404 return false; 405 } 406 407 private boolean matchesMobileWildcard(NetworkIdentity ident) { 408 if (ident.mType == TYPE_WIMAX) { 409 return true; 410 } else { 411 return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered); 412 } 413 } 414 415 private boolean matchesWifiWildcard(NetworkIdentity ident) { 416 switch (ident.mType) { 417 case TYPE_WIFI: 418 case TYPE_WIFI_P2P: 419 return true; 420 default: 421 return false; 422 } 423 } 424 425 /** 426 * Check if matches Bluetooth network template. 427 */ 428 private boolean matchesBluetooth(NetworkIdentity ident) { 429 if (ident.mType == TYPE_BLUETOOTH) { 430 return true; 431 } 432 return false; 433 } 434 435 /** 436 * Check if matches Proxy network template. 437 */ 438 private boolean matchesProxy(NetworkIdentity ident) { 439 return ident.mType == TYPE_PROXY; 440 } 441 442 private static String getMatchRuleName(int matchRule) { 443 switch (matchRule) { 444 case MATCH_MOBILE_3G_LOWER: 445 return "MOBILE_3G_LOWER"; 446 case MATCH_MOBILE_4G: 447 return "MOBILE_4G"; 448 case MATCH_MOBILE_ALL: 449 return "MOBILE_ALL"; 450 case MATCH_WIFI: 451 return "WIFI"; 452 case MATCH_ETHERNET: 453 return "ETHERNET"; 454 case MATCH_MOBILE_WILDCARD: 455 return "MOBILE_WILDCARD"; 456 case MATCH_WIFI_WILDCARD: 457 return "WIFI_WILDCARD"; 458 case MATCH_BLUETOOTH: 459 return "BLUETOOTH"; 460 case MATCH_PROXY: 461 return "PROXY"; 462 default: 463 return "UNKNOWN(" + matchRule + ")"; 464 } 465 } 466 467 private static void ensureSubtypeAvailable() { 468 if (COMBINE_SUBTYPE_ENABLED) { 469 throw new IllegalArgumentException( 470 "Unable to enforce 3G_LOWER template on combined data."); 471 } 472 } 473 474 /** 475 * Examine the given template and normalize if it refers to a "merged" 476 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 477 * for key purposes, and expand the template to match all other merged 478 * subscribers. 479 * <p> 480 * For example, given an incoming template matching B, and the currently 481 * active merge set [A,B], we'd return a new template that primarily matches 482 * A, but also matches B. 483 */ 484 public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { 485 if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { 486 // Requested template subscriber is part of the merge group; return 487 // a template that matches all merged subscribers. 488 return new NetworkTemplate(template.mMatchRule, merged[0], merged, 489 template.mNetworkId); 490 } else { 491 return template; 492 } 493 } 494 495 public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { 496 @Override 497 public NetworkTemplate createFromParcel(Parcel in) { 498 return new NetworkTemplate(in); 499 } 500 501 @Override 502 public NetworkTemplate[] newArray(int size) { 503 return new NetworkTemplate[size]; 504 } 505 }; 506 507 public byte[] getBytesForBackup() throws IOException { 508 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 509 DataOutputStream out = new DataOutputStream(baos); 510 511 out.writeInt(BACKUP_VERSION); 512 513 out.writeInt(mMatchRule); 514 BackupUtils.writeString(out, mSubscriberId); 515 BackupUtils.writeString(out, mNetworkId); 516 517 return baos.toByteArray(); 518 } 519 520 public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in) 521 throws IOException, BackupUtils.BadVersionException { 522 int version = in.readInt(); 523 if (version < 1 || version > BACKUP_VERSION) { 524 throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); 525 } 526 527 int matchRule = in.readInt(); 528 String subscriberId = BackupUtils.readString(in); 529 String networkId = BackupUtils.readString(in); 530 531 if (!isKnownMatchRule(matchRule)) { 532 throw new BackupUtils.BadVersionException( 533 "Restored network template contains unknown match rule " + matchRule); 534 } 535 536 return new NetworkTemplate(matchRule, subscriberId, networkId); 537 } 538 } 539