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