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_PROXY; 22 import static android.net.ConnectivityManager.TYPE_MOBILE; 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 38 import com.android.internal.annotations.VisibleForTesting; 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 /** 56 * Current Version of the Backup Serializer. 57 */ 58 private static final int BACKUP_VERSION = 1; 59 60 public static final int MATCH_MOBILE_ALL = 1; 61 @Deprecated 62 public static final int MATCH_MOBILE_3G_LOWER = 2; 63 @Deprecated 64 public static final int MATCH_MOBILE_4G = 3; 65 public static final int MATCH_WIFI = 4; 66 public static final int MATCH_ETHERNET = 5; 67 public static final int MATCH_MOBILE_WILDCARD = 6; 68 public static final int MATCH_WIFI_WILDCARD = 7; 69 public static final int MATCH_BLUETOOTH = 8; 70 public static final int MATCH_PROXY = 9; 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 /** 151 * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style 152 * networks together. 153 */ 154 public static NetworkTemplate buildTemplateProxy() { 155 return new NetworkTemplate(MATCH_PROXY, null, null); 156 } 157 158 private final int mMatchRule; 159 private final String mSubscriberId; 160 161 /** 162 * Ugh, templates are designed to target a single subscriber, but we might 163 * need to match several "merged" subscribers. These are the subscribers 164 * that should be considered to match this template. 165 * <p> 166 * Since the merge set is dynamic, it should <em>not</em> be persisted or 167 * used for determining equality. 168 */ 169 private final String[] mMatchSubscriberIds; 170 171 private final String mNetworkId; 172 173 public NetworkTemplate(int matchRule, String subscriberId, String networkId) { 174 this(matchRule, subscriberId, new String[] { subscriberId }, networkId); 175 } 176 177 public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, 178 String networkId) { 179 mMatchRule = matchRule; 180 mSubscriberId = subscriberId; 181 mMatchSubscriberIds = matchSubscriberIds; 182 mNetworkId = networkId; 183 } 184 185 private NetworkTemplate(Parcel in) { 186 mMatchRule = in.readInt(); 187 mSubscriberId = in.readString(); 188 mMatchSubscriberIds = in.createStringArray(); 189 mNetworkId = in.readString(); 190 } 191 192 @Override 193 public void writeToParcel(Parcel dest, int flags) { 194 dest.writeInt(mMatchRule); 195 dest.writeString(mSubscriberId); 196 dest.writeStringArray(mMatchSubscriberIds); 197 dest.writeString(mNetworkId); 198 } 199 200 @Override 201 public int describeContents() { 202 return 0; 203 } 204 205 @Override 206 public String toString() { 207 final StringBuilder builder = new StringBuilder("NetworkTemplate: "); 208 builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); 209 if (mSubscriberId != null) { 210 builder.append(", subscriberId=").append( 211 NetworkIdentity.scrubSubscriberId(mSubscriberId)); 212 } 213 if (mMatchSubscriberIds != null) { 214 builder.append(", matchSubscriberIds=").append( 215 Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds))); 216 } 217 if (mNetworkId != null) { 218 builder.append(", networkId=").append(mNetworkId); 219 } 220 return builder.toString(); 221 } 222 223 @Override 224 public int hashCode() { 225 return Objects.hash(mMatchRule, mSubscriberId, mNetworkId); 226 } 227 228 @Override 229 public boolean equals(Object obj) { 230 if (obj instanceof NetworkTemplate) { 231 final NetworkTemplate other = (NetworkTemplate) obj; 232 return mMatchRule == other.mMatchRule 233 && Objects.equals(mSubscriberId, other.mSubscriberId) 234 && Objects.equals(mNetworkId, other.mNetworkId); 235 } 236 return false; 237 } 238 239 public boolean isMatchRuleMobile() { 240 switch (mMatchRule) { 241 case MATCH_MOBILE_3G_LOWER: 242 case MATCH_MOBILE_4G: 243 case MATCH_MOBILE_ALL: 244 case MATCH_MOBILE_WILDCARD: 245 return true; 246 default: 247 return false; 248 } 249 } 250 251 public boolean isPersistable() { 252 switch (mMatchRule) { 253 case MATCH_MOBILE_WILDCARD: 254 case MATCH_WIFI_WILDCARD: 255 return false; 256 default: 257 return true; 258 } 259 } 260 261 public int getMatchRule() { 262 return mMatchRule; 263 } 264 265 public String getSubscriberId() { 266 return mSubscriberId; 267 } 268 269 public String getNetworkId() { 270 return mNetworkId; 271 } 272 273 /** 274 * Test if given {@link NetworkIdentity} matches this template. 275 */ 276 public boolean matches(NetworkIdentity ident) { 277 switch (mMatchRule) { 278 case MATCH_MOBILE_ALL: 279 return matchesMobile(ident); 280 case MATCH_MOBILE_3G_LOWER: 281 return matchesMobile3gLower(ident); 282 case MATCH_MOBILE_4G: 283 return matchesMobile4g(ident); 284 case MATCH_WIFI: 285 return matchesWifi(ident); 286 case MATCH_ETHERNET: 287 return matchesEthernet(ident); 288 case MATCH_MOBILE_WILDCARD: 289 return matchesMobileWildcard(ident); 290 case MATCH_WIFI_WILDCARD: 291 return matchesWifiWildcard(ident); 292 case MATCH_BLUETOOTH: 293 return matchesBluetooth(ident); 294 case MATCH_PROXY: 295 return matchesProxy(ident); 296 default: 297 throw new IllegalArgumentException("unknown network template"); 298 } 299 } 300 301 /** 302 * Check if mobile network with matching IMSI. 303 */ 304 private boolean matchesMobile(NetworkIdentity ident) { 305 if (ident.mType == TYPE_WIMAX) { 306 // TODO: consider matching against WiMAX subscriber identity 307 return true; 308 } else { 309 return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) 310 && !ArrayUtils.isEmpty(mMatchSubscriberIds) 311 && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); 312 } 313 } 314 315 /** 316 * Check if mobile network classified 3G or lower with matching IMSI. 317 */ 318 @Deprecated 319 private boolean matchesMobile3gLower(NetworkIdentity ident) { 320 ensureSubtypeAvailable(); 321 if (ident.mType == TYPE_WIMAX) { 322 return false; 323 } else if (matchesMobile(ident)) { 324 switch (getNetworkClass(ident.mSubType)) { 325 case NETWORK_CLASS_UNKNOWN: 326 case NETWORK_CLASS_2_G: 327 case NETWORK_CLASS_3_G: 328 return true; 329 } 330 } 331 return false; 332 } 333 334 /** 335 * Check if mobile network classified 4G with matching IMSI. 336 */ 337 @Deprecated 338 private boolean matchesMobile4g(NetworkIdentity ident) { 339 ensureSubtypeAvailable(); 340 if (ident.mType == TYPE_WIMAX) { 341 // TODO: consider matching against WiMAX subscriber identity 342 return true; 343 } else if (matchesMobile(ident)) { 344 switch (getNetworkClass(ident.mSubType)) { 345 case NETWORK_CLASS_4_G: 346 return true; 347 } 348 } 349 return false; 350 } 351 352 /** 353 * Check if matches Wi-Fi network template. 354 */ 355 private boolean matchesWifi(NetworkIdentity ident) { 356 switch (ident.mType) { 357 case TYPE_WIFI: 358 return Objects.equals( 359 removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId)); 360 default: 361 return false; 362 } 363 } 364 365 /** 366 * Check if matches Ethernet network template. 367 */ 368 private boolean matchesEthernet(NetworkIdentity ident) { 369 if (ident.mType == TYPE_ETHERNET) { 370 return true; 371 } 372 return false; 373 } 374 375 private boolean matchesMobileWildcard(NetworkIdentity ident) { 376 if (ident.mType == TYPE_WIMAX) { 377 return true; 378 } else { 379 return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered); 380 } 381 } 382 383 private boolean matchesWifiWildcard(NetworkIdentity ident) { 384 switch (ident.mType) { 385 case TYPE_WIFI: 386 case TYPE_WIFI_P2P: 387 return true; 388 default: 389 return false; 390 } 391 } 392 393 /** 394 * Check if matches Bluetooth network template. 395 */ 396 private boolean matchesBluetooth(NetworkIdentity ident) { 397 if (ident.mType == TYPE_BLUETOOTH) { 398 return true; 399 } 400 return false; 401 } 402 403 /** 404 * Check if matches Proxy network template. 405 */ 406 private boolean matchesProxy(NetworkIdentity ident) { 407 return ident.mType == TYPE_PROXY; 408 } 409 410 private static String getMatchRuleName(int matchRule) { 411 switch (matchRule) { 412 case MATCH_MOBILE_3G_LOWER: 413 return "MOBILE_3G_LOWER"; 414 case MATCH_MOBILE_4G: 415 return "MOBILE_4G"; 416 case MATCH_MOBILE_ALL: 417 return "MOBILE_ALL"; 418 case MATCH_WIFI: 419 return "WIFI"; 420 case MATCH_ETHERNET: 421 return "ETHERNET"; 422 case MATCH_MOBILE_WILDCARD: 423 return "MOBILE_WILDCARD"; 424 case MATCH_WIFI_WILDCARD: 425 return "WIFI_WILDCARD"; 426 case MATCH_BLUETOOTH: 427 return "BLUETOOTH"; 428 case MATCH_PROXY: 429 return "PROXY"; 430 default: 431 return "UNKNOWN"; 432 } 433 } 434 435 private static void ensureSubtypeAvailable() { 436 if (COMBINE_SUBTYPE_ENABLED) { 437 throw new IllegalArgumentException( 438 "Unable to enforce 3G_LOWER template on combined data."); 439 } 440 } 441 442 /** 443 * Examine the given template and normalize if it refers to a "merged" 444 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 445 * for key purposes, and expand the template to match all other merged 446 * subscribers. 447 * <p> 448 * For example, given an incoming template matching B, and the currently 449 * active merge set [A,B], we'd return a new template that primarily matches 450 * A, but also matches B. 451 */ 452 public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { 453 if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { 454 // Requested template subscriber is part of the merge group; return 455 // a template that matches all merged subscribers. 456 return new NetworkTemplate(template.mMatchRule, merged[0], merged, 457 template.mNetworkId); 458 } else { 459 return template; 460 } 461 } 462 463 public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { 464 @Override 465 public NetworkTemplate createFromParcel(Parcel in) { 466 return new NetworkTemplate(in); 467 } 468 469 @Override 470 public NetworkTemplate[] newArray(int size) { 471 return new NetworkTemplate[size]; 472 } 473 }; 474 475 public byte[] getBytesForBackup() throws IOException { 476 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 477 DataOutputStream out = new DataOutputStream(baos); 478 479 out.writeInt(BACKUP_VERSION); 480 481 out.writeInt(mMatchRule); 482 BackupUtils.writeString(out, mSubscriberId); 483 BackupUtils.writeString(out, mNetworkId); 484 485 return baos.toByteArray(); 486 } 487 488 public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in) 489 throws IOException, BackupUtils.BadVersionException { 490 int version = in.readInt(); 491 if (version < 1 || version > BACKUP_VERSION) { 492 throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); 493 } 494 495 int matchRule = in.readInt(); 496 String subscriberId = BackupUtils.readString(in); 497 String networkId = BackupUtils.readString(in); 498 499 return new NetworkTemplate(matchRule, subscriberId, networkId); 500 } 501 } 502