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_WIFI; 22 import static android.net.ConnectivityManager.TYPE_WIFI_P2P; 23 import static android.net.ConnectivityManager.TYPE_WIMAX; 24 import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED; 25 import static android.net.wifi.WifiInfo.removeDoubleQuotes; 26 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; 27 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; 28 import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; 29 import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; 30 import static android.telephony.TelephonyManager.getNetworkClass; 31 import static com.android.internal.util.ArrayUtils.contains; 32 33 import android.content.res.Resources; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.util.ArrayUtils; 39 40 import java.util.Arrays; 41 import java.util.Objects; 42 43 /** 44 * Template definition used to generically match {@link NetworkIdentity}, 45 * usually when collecting statistics. 46 * 47 * @hide 48 */ 49 public 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 forceAllNetworkTypes()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 */ buildTemplateMobileAll(String subscriberId)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 buildTemplateMobile3gLower(String subscriberId)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 buildTemplateMobile4g(String subscriberId)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 */ buildTemplateMobileWildcard()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 */ buildTemplateWifiWildcard()117 public static NetworkTemplate buildTemplateWifiWildcard() { 118 return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null); 119 } 120 121 @Deprecated buildTemplateWifi()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 */ buildTemplateWifi(String networkId)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 */ buildTemplateEthernet()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 */ buildTemplateBluetooth()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 NetworkTemplate(int matchRule, String subscriberId, String networkId)165 public NetworkTemplate(int matchRule, String subscriberId, String networkId) { 166 this(matchRule, subscriberId, new String[] { subscriberId }, networkId); 167 } 168 NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId)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 NetworkTemplate(Parcel in)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 writeToParcel(Parcel dest, int flags)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 describeContents()193 public int describeContents() { 194 return 0; 195 } 196 197 @Override toString()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 hashCode()216 public int hashCode() { 217 return Objects.hash(mMatchRule, mSubscriberId, mNetworkId); 218 } 219 220 @Override equals(Object obj)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 isMatchRuleMobile()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 getMatchRule()243 public int getMatchRule() { 244 return mMatchRule; 245 } 246 getSubscriberId()247 public String getSubscriberId() { 248 return mSubscriberId; 249 } 250 getNetworkId()251 public String getNetworkId() { 252 return mNetworkId; 253 } 254 255 /** 256 * Test if given {@link NetworkIdentity} matches this template. 257 */ matches(NetworkIdentity ident)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 */ matchesMobile(NetworkIdentity ident)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.contains(mMatchSubscriberIds, ident.mSubscriberId); 292 } 293 } 294 295 /** 296 * Check if mobile network classified 3G or lower with matching IMSI. 297 */ 298 @Deprecated matchesMobile3gLower(NetworkIdentity ident)299 private boolean matchesMobile3gLower(NetworkIdentity ident) { 300 ensureSubtypeAvailable(); 301 if (ident.mType == TYPE_WIMAX) { 302 return false; 303 } else if (matchesMobile(ident)) { 304 switch (getNetworkClass(ident.mSubType)) { 305 case NETWORK_CLASS_UNKNOWN: 306 case NETWORK_CLASS_2_G: 307 case NETWORK_CLASS_3_G: 308 return true; 309 } 310 } 311 return false; 312 } 313 314 /** 315 * Check if mobile network classified 4G with matching IMSI. 316 */ 317 @Deprecated matchesMobile4g(NetworkIdentity ident)318 private boolean matchesMobile4g(NetworkIdentity ident) { 319 ensureSubtypeAvailable(); 320 if (ident.mType == TYPE_WIMAX) { 321 // TODO: consider matching against WiMAX subscriber identity 322 return true; 323 } else if (matchesMobile(ident)) { 324 switch (getNetworkClass(ident.mSubType)) { 325 case NETWORK_CLASS_4_G: 326 return true; 327 } 328 } 329 return false; 330 } 331 332 /** 333 * Check if matches Wi-Fi network template. 334 */ matchesWifi(NetworkIdentity ident)335 private boolean matchesWifi(NetworkIdentity ident) { 336 switch (ident.mType) { 337 case TYPE_WIFI: 338 return Objects.equals( 339 removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId)); 340 default: 341 return false; 342 } 343 } 344 345 /** 346 * Check if matches Ethernet network template. 347 */ matchesEthernet(NetworkIdentity ident)348 private boolean matchesEthernet(NetworkIdentity ident) { 349 if (ident.mType == TYPE_ETHERNET) { 350 return true; 351 } 352 return false; 353 } 354 matchesMobileWildcard(NetworkIdentity ident)355 private boolean matchesMobileWildcard(NetworkIdentity ident) { 356 if (ident.mType == TYPE_WIMAX) { 357 return true; 358 } else { 359 return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType); 360 } 361 } 362 matchesWifiWildcard(NetworkIdentity ident)363 private boolean matchesWifiWildcard(NetworkIdentity ident) { 364 switch (ident.mType) { 365 case TYPE_WIFI: 366 case TYPE_WIFI_P2P: 367 return true; 368 default: 369 return false; 370 } 371 } 372 373 /** 374 * Check if matches Bluetooth network template. 375 */ matchesBluetooth(NetworkIdentity ident)376 private boolean matchesBluetooth(NetworkIdentity ident) { 377 if (ident.mType == TYPE_BLUETOOTH) { 378 return true; 379 } 380 return false; 381 } 382 getMatchRuleName(int matchRule)383 private static String getMatchRuleName(int matchRule) { 384 switch (matchRule) { 385 case MATCH_MOBILE_3G_LOWER: 386 return "MOBILE_3G_LOWER"; 387 case MATCH_MOBILE_4G: 388 return "MOBILE_4G"; 389 case MATCH_MOBILE_ALL: 390 return "MOBILE_ALL"; 391 case MATCH_WIFI: 392 return "WIFI"; 393 case MATCH_ETHERNET: 394 return "ETHERNET"; 395 case MATCH_MOBILE_WILDCARD: 396 return "MOBILE_WILDCARD"; 397 case MATCH_WIFI_WILDCARD: 398 return "WIFI_WILDCARD"; 399 case MATCH_BLUETOOTH: 400 return "BLUETOOTH"; 401 default: 402 return "UNKNOWN"; 403 } 404 } 405 ensureSubtypeAvailable()406 private static void ensureSubtypeAvailable() { 407 if (COMBINE_SUBTYPE_ENABLED) { 408 throw new IllegalArgumentException( 409 "Unable to enforce 3G_LOWER template on combined data."); 410 } 411 } 412 413 /** 414 * Examine the given template and normalize if it refers to a "merged" 415 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 416 * for key purposes, and expand the template to match all other merged 417 * subscribers. 418 * <p> 419 * For example, given an incoming template matching B, and the currently 420 * active merge set [A,B], we'd return a new template that primarily matches 421 * A, but also matches B. 422 */ normalize(NetworkTemplate template, String[] merged)423 public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { 424 if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) { 425 // Requested template subscriber is part of the merge group; return 426 // a template that matches all merged subscribers. 427 return new NetworkTemplate(template.mMatchRule, merged[0], merged, 428 template.mNetworkId); 429 } else { 430 return template; 431 } 432 } 433 434 public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { 435 @Override 436 public NetworkTemplate createFromParcel(Parcel in) { 437 return new NetworkTemplate(in); 438 } 439 440 @Override 441 public NetworkTemplate[] newArray(int size) { 442 return new NetworkTemplate[size]; 443 } 444 }; 445 } 446