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