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.sanitizeSsid; 36 37 import android.annotation.Nullable; 38 import android.compat.annotation.UnsupportedAppUsage; 39 import android.os.Parcel; 40 import android.os.Parcelable; 41 import android.telephony.Annotation.NetworkType; 42 import android.telephony.TelephonyManager; 43 import android.text.TextUtils; 44 import android.util.BackupUtils; 45 import android.util.Log; 46 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.ArrayUtils; 49 50 import java.io.ByteArrayOutputStream; 51 import java.io.DataInputStream; 52 import java.io.DataOutputStream; 53 import java.io.IOException; 54 import java.util.Arrays; 55 import java.util.List; 56 import java.util.Objects; 57 58 /** 59 * Predicate used to match {@link NetworkIdentity}, usually when collecting 60 * statistics. (It should probably have been named {@code NetworkPredicate}.) 61 * 62 * @hide 63 */ 64 public class NetworkTemplate implements Parcelable { 65 private static final String TAG = "NetworkTemplate"; 66 67 /** 68 * Current Version of the Backup Serializer. 69 */ 70 private static final int BACKUP_VERSION = 1; 71 72 public static final int MATCH_MOBILE = 1; 73 public static final int MATCH_WIFI = 4; 74 public static final int MATCH_ETHERNET = 5; 75 public static final int MATCH_MOBILE_WILDCARD = 6; 76 public static final int MATCH_WIFI_WILDCARD = 7; 77 public static final int MATCH_BLUETOOTH = 8; 78 public static final int MATCH_PROXY = 9; 79 80 /** 81 * Include all network types when filtering. This is meant to merge in with the 82 * {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync. 83 * 84 * @hide 85 */ 86 public static final int NETWORK_TYPE_ALL = -1; 87 /** 88 * Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is 89 * still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along 90 * with NR state as connected. This should not be overlapped with any of the 91 * {@code TelephonyManager.NETWORK_TYPE_*} constants. 92 * 93 * @hide 94 */ 95 public static final int NETWORK_TYPE_5G_NSA = -2; 96 isKnownMatchRule(final int rule)97 private static boolean isKnownMatchRule(final int rule) { 98 switch (rule) { 99 case MATCH_MOBILE: 100 case MATCH_WIFI: 101 case MATCH_ETHERNET: 102 case MATCH_MOBILE_WILDCARD: 103 case MATCH_WIFI_WILDCARD: 104 case MATCH_BLUETOOTH: 105 case MATCH_PROXY: 106 return true; 107 108 default: 109 return false; 110 } 111 } 112 113 private static boolean sForceAllNetworkTypes = false; 114 115 /** 116 * Results in matching against all mobile network types. 117 * 118 * <p>See {@link #matchesMobile} and {@link matchesMobileWildcard}. 119 */ 120 @VisibleForTesting forceAllNetworkTypes()121 public static void forceAllNetworkTypes() { 122 sForceAllNetworkTypes = true; 123 } 124 125 /** Resets the affect of {@link #forceAllNetworkTypes}. */ 126 @VisibleForTesting resetForceAllNetworkTypes()127 public static void resetForceAllNetworkTypes() { 128 sForceAllNetworkTypes = false; 129 } 130 131 /** 132 * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with 133 * the given IMSI. 134 */ 135 @UnsupportedAppUsage buildTemplateMobileAll(String subscriberId)136 public static NetworkTemplate buildTemplateMobileAll(String subscriberId) { 137 return new NetworkTemplate(MATCH_MOBILE, subscriberId, null); 138 } 139 140 /** 141 * Template to match cellular networks with the given IMSI and {@code ratType}. 142 * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering. 143 * See {@code TelephonyManager.NETWORK_TYPE_*}. 144 */ buildTemplateMobileWithRatType(@ullable String subscriberId, @NetworkType int ratType)145 public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId, 146 @NetworkType int ratType) { 147 if (TextUtils.isEmpty(subscriberId)) { 148 return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null, 149 METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); 150 } 151 return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null, 152 METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); 153 } 154 155 /** 156 * Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks, 157 * regardless of IMSI. 158 */ 159 @UnsupportedAppUsage buildTemplateMobileWildcard()160 public static NetworkTemplate buildTemplateMobileWildcard() { 161 return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null); 162 } 163 164 /** 165 * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks, 166 * regardless of SSID. 167 */ 168 @UnsupportedAppUsage buildTemplateWifiWildcard()169 public static NetworkTemplate buildTemplateWifiWildcard() { 170 return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null); 171 } 172 173 @Deprecated 174 @UnsupportedAppUsage buildTemplateWifi()175 public static NetworkTemplate buildTemplateWifi() { 176 return buildTemplateWifiWildcard(); 177 } 178 179 /** 180 * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the 181 * given SSID. 182 */ buildTemplateWifi(String networkId)183 public static NetworkTemplate buildTemplateWifi(String networkId) { 184 return new NetworkTemplate(MATCH_WIFI, null, networkId); 185 } 186 187 /** 188 * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style 189 * networks together. 190 */ 191 @UnsupportedAppUsage buildTemplateEthernet()192 public static NetworkTemplate buildTemplateEthernet() { 193 return new NetworkTemplate(MATCH_ETHERNET, null, null); 194 } 195 196 /** 197 * Template to combine all {@link ConnectivityManager#TYPE_BLUETOOTH} style 198 * networks together. 199 */ buildTemplateBluetooth()200 public static NetworkTemplate buildTemplateBluetooth() { 201 return new NetworkTemplate(MATCH_BLUETOOTH, null, null); 202 } 203 204 /** 205 * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style 206 * networks together. 207 */ buildTemplateProxy()208 public static NetworkTemplate buildTemplateProxy() { 209 return new NetworkTemplate(MATCH_PROXY, null, null); 210 } 211 212 private final int mMatchRule; 213 private final String mSubscriberId; 214 215 /** 216 * Ugh, templates are designed to target a single subscriber, but we might 217 * need to match several "merged" subscribers. These are the subscribers 218 * that should be considered to match this template. 219 * <p> 220 * Since the merge set is dynamic, it should <em>not</em> be persisted or 221 * used for determining equality. 222 */ 223 private final String[] mMatchSubscriberIds; 224 225 private final String mNetworkId; 226 227 // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*. 228 private final int mMetered; 229 private final int mRoaming; 230 private final int mDefaultNetwork; 231 private final int mSubType; 232 233 @UnsupportedAppUsage NetworkTemplate(int matchRule, String subscriberId, String networkId)234 public NetworkTemplate(int matchRule, String subscriberId, String networkId) { 235 this(matchRule, subscriberId, new String[] { subscriberId }, networkId); 236 } 237 NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId)238 public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, 239 String networkId) { 240 this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL, 241 DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL); 242 } 243 NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId, int metered, int roaming, int defaultNetwork, int subType)244 public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, 245 String networkId, int metered, int roaming, int defaultNetwork, int subType) { 246 mMatchRule = matchRule; 247 mSubscriberId = subscriberId; 248 mMatchSubscriberIds = matchSubscriberIds; 249 mNetworkId = networkId; 250 mMetered = metered; 251 mRoaming = roaming; 252 mDefaultNetwork = defaultNetwork; 253 mSubType = subType; 254 255 if (!isKnownMatchRule(matchRule)) { 256 Log.e(TAG, "Unknown network template rule " + matchRule 257 + " will not match any identity."); 258 } 259 } 260 NetworkTemplate(Parcel in)261 private NetworkTemplate(Parcel in) { 262 mMatchRule = in.readInt(); 263 mSubscriberId = in.readString(); 264 mMatchSubscriberIds = in.createStringArray(); 265 mNetworkId = in.readString(); 266 mMetered = in.readInt(); 267 mRoaming = in.readInt(); 268 mDefaultNetwork = in.readInt(); 269 mSubType = in.readInt(); 270 } 271 272 @Override writeToParcel(Parcel dest, int flags)273 public void writeToParcel(Parcel dest, int flags) { 274 dest.writeInt(mMatchRule); 275 dest.writeString(mSubscriberId); 276 dest.writeStringArray(mMatchSubscriberIds); 277 dest.writeString(mNetworkId); 278 dest.writeInt(mMetered); 279 dest.writeInt(mRoaming); 280 dest.writeInt(mDefaultNetwork); 281 dest.writeInt(mSubType); 282 } 283 284 @Override describeContents()285 public int describeContents() { 286 return 0; 287 } 288 289 @Override toString()290 public String toString() { 291 final StringBuilder builder = new StringBuilder("NetworkTemplate: "); 292 builder.append("matchRule=").append(getMatchRuleName(mMatchRule)); 293 if (mSubscriberId != null) { 294 builder.append(", subscriberId=").append( 295 NetworkIdentity.scrubSubscriberId(mSubscriberId)); 296 } 297 if (mMatchSubscriberIds != null) { 298 builder.append(", matchSubscriberIds=").append( 299 Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds))); 300 } 301 if (mNetworkId != null) { 302 builder.append(", networkId=").append(mNetworkId); 303 } 304 if (mMetered != METERED_ALL) { 305 builder.append(", metered=").append(NetworkStats.meteredToString(mMetered)); 306 } 307 if (mRoaming != ROAMING_ALL) { 308 builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming)); 309 } 310 if (mDefaultNetwork != DEFAULT_NETWORK_ALL) { 311 builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString( 312 mDefaultNetwork)); 313 } 314 if (mSubType != NETWORK_TYPE_ALL) { 315 builder.append(", subType=").append(mSubType); 316 } 317 return builder.toString(); 318 } 319 320 @Override hashCode()321 public int hashCode() { 322 return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming, 323 mDefaultNetwork, mSubType); 324 } 325 326 @Override equals(Object obj)327 public boolean equals(Object obj) { 328 if (obj instanceof NetworkTemplate) { 329 final NetworkTemplate other = (NetworkTemplate) obj; 330 return mMatchRule == other.mMatchRule 331 && Objects.equals(mSubscriberId, other.mSubscriberId) 332 && Objects.equals(mNetworkId, other.mNetworkId) 333 && mMetered == other.mMetered 334 && mRoaming == other.mRoaming 335 && mDefaultNetwork == other.mDefaultNetwork 336 && mSubType == other.mSubType; 337 } 338 return false; 339 } 340 isMatchRuleMobile()341 public boolean isMatchRuleMobile() { 342 switch (mMatchRule) { 343 case MATCH_MOBILE: 344 case MATCH_MOBILE_WILDCARD: 345 return true; 346 default: 347 return false; 348 } 349 } 350 isPersistable()351 public boolean isPersistable() { 352 switch (mMatchRule) { 353 case MATCH_MOBILE_WILDCARD: 354 case MATCH_WIFI_WILDCARD: 355 return false; 356 default: 357 return true; 358 } 359 } 360 361 @UnsupportedAppUsage getMatchRule()362 public int getMatchRule() { 363 return mMatchRule; 364 } 365 366 @UnsupportedAppUsage getSubscriberId()367 public String getSubscriberId() { 368 return mSubscriberId; 369 } 370 getNetworkId()371 public String getNetworkId() { 372 return mNetworkId; 373 } 374 375 /** 376 * Test if given {@link NetworkIdentity} matches this template. 377 */ matches(NetworkIdentity ident)378 public boolean matches(NetworkIdentity ident) { 379 if (!matchesMetered(ident)) return false; 380 if (!matchesRoaming(ident)) return false; 381 if (!matchesDefaultNetwork(ident)) return false; 382 383 switch (mMatchRule) { 384 case MATCH_MOBILE: 385 return matchesMobile(ident); 386 case MATCH_WIFI: 387 return matchesWifi(ident); 388 case MATCH_ETHERNET: 389 return matchesEthernet(ident); 390 case MATCH_MOBILE_WILDCARD: 391 return matchesMobileWildcard(ident); 392 case MATCH_WIFI_WILDCARD: 393 return matchesWifiWildcard(ident); 394 case MATCH_BLUETOOTH: 395 return matchesBluetooth(ident); 396 case MATCH_PROXY: 397 return matchesProxy(ident); 398 default: 399 // We have no idea what kind of network template we are, so we 400 // just claim not to match anything. 401 return false; 402 } 403 } 404 matchesMetered(NetworkIdentity ident)405 private boolean matchesMetered(NetworkIdentity ident) { 406 return (mMetered == METERED_ALL) 407 || (mMetered == METERED_YES && ident.mMetered) 408 || (mMetered == METERED_NO && !ident.mMetered); 409 } 410 matchesRoaming(NetworkIdentity ident)411 private boolean matchesRoaming(NetworkIdentity ident) { 412 return (mRoaming == ROAMING_ALL) 413 || (mRoaming == ROAMING_YES && ident.mRoaming) 414 || (mRoaming == ROAMING_NO && !ident.mRoaming); 415 } 416 matchesDefaultNetwork(NetworkIdentity ident)417 private boolean matchesDefaultNetwork(NetworkIdentity ident) { 418 return (mDefaultNetwork == DEFAULT_NETWORK_ALL) 419 || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork) 420 || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork); 421 } 422 matchesCollapsedRatType(NetworkIdentity ident)423 private boolean matchesCollapsedRatType(NetworkIdentity ident) { 424 return mSubType == NETWORK_TYPE_ALL 425 || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType); 426 } 427 matchesSubscriberId(String subscriberId)428 public boolean matchesSubscriberId(String subscriberId) { 429 return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); 430 } 431 432 /** 433 * Check if mobile network with matching IMSI. 434 */ matchesMobile(NetworkIdentity ident)435 private boolean matchesMobile(NetworkIdentity ident) { 436 if (ident.mType == TYPE_WIMAX) { 437 // TODO: consider matching against WiMAX subscriber identity 438 return true; 439 } else { 440 // Only metered mobile network would be matched regardless of metered filter. 441 // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650. 442 // TODO: Respect metered filter and remove mMetered condition. 443 return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) 444 && !ArrayUtils.isEmpty(mMatchSubscriberIds) 445 && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId) 446 && matchesCollapsedRatType(ident); 447 } 448 } 449 450 /** 451 * Get a Radio Access Technology(RAT) type that is representative of a group of RAT types. 452 * The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}. 453 * 454 * @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}. 455 */ 456 // TODO: 1. Consider move this to TelephonyManager if used by other modules. 457 // 2. Consider make this configurable. 458 // 3. Use TelephonyManager APIs when available. getCollapsedRatType(int ratType)459 public static int getCollapsedRatType(int ratType) { 460 switch (ratType) { 461 case TelephonyManager.NETWORK_TYPE_GPRS: 462 case TelephonyManager.NETWORK_TYPE_GSM: 463 case TelephonyManager.NETWORK_TYPE_EDGE: 464 case TelephonyManager.NETWORK_TYPE_IDEN: 465 case TelephonyManager.NETWORK_TYPE_CDMA: 466 case TelephonyManager.NETWORK_TYPE_1xRTT: 467 return TelephonyManager.NETWORK_TYPE_GSM; 468 case TelephonyManager.NETWORK_TYPE_EVDO_0: 469 case TelephonyManager.NETWORK_TYPE_EVDO_A: 470 case TelephonyManager.NETWORK_TYPE_EVDO_B: 471 case TelephonyManager.NETWORK_TYPE_EHRPD: 472 case TelephonyManager.NETWORK_TYPE_UMTS: 473 case TelephonyManager.NETWORK_TYPE_HSDPA: 474 case TelephonyManager.NETWORK_TYPE_HSUPA: 475 case TelephonyManager.NETWORK_TYPE_HSPA: 476 case TelephonyManager.NETWORK_TYPE_HSPAP: 477 case TelephonyManager.NETWORK_TYPE_TD_SCDMA: 478 return TelephonyManager.NETWORK_TYPE_UMTS; 479 case TelephonyManager.NETWORK_TYPE_LTE: 480 case TelephonyManager.NETWORK_TYPE_IWLAN: 481 return TelephonyManager.NETWORK_TYPE_LTE; 482 case TelephonyManager.NETWORK_TYPE_NR: 483 return TelephonyManager.NETWORK_TYPE_NR; 484 // Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}. 485 case NetworkTemplate.NETWORK_TYPE_5G_NSA: 486 return NetworkTemplate.NETWORK_TYPE_5G_NSA; 487 default: 488 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 489 } 490 } 491 492 /** 493 * Check if matches Wi-Fi network template. 494 */ matchesWifi(NetworkIdentity ident)495 private boolean matchesWifi(NetworkIdentity ident) { 496 switch (ident.mType) { 497 case TYPE_WIFI: 498 return Objects.equals( 499 sanitizeSsid(mNetworkId), sanitizeSsid(ident.mNetworkId)); 500 default: 501 return false; 502 } 503 } 504 505 /** 506 * Check if matches Ethernet network template. 507 */ matchesEthernet(NetworkIdentity ident)508 private boolean matchesEthernet(NetworkIdentity ident) { 509 if (ident.mType == TYPE_ETHERNET) { 510 return true; 511 } 512 return false; 513 } 514 matchesMobileWildcard(NetworkIdentity ident)515 private boolean matchesMobileWildcard(NetworkIdentity ident) { 516 if (ident.mType == TYPE_WIMAX) { 517 return true; 518 } else { 519 return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) 520 && matchesCollapsedRatType(ident); 521 } 522 } 523 matchesWifiWildcard(NetworkIdentity ident)524 private boolean matchesWifiWildcard(NetworkIdentity ident) { 525 switch (ident.mType) { 526 case TYPE_WIFI: 527 case TYPE_WIFI_P2P: 528 return true; 529 default: 530 return false; 531 } 532 } 533 534 /** 535 * Check if matches Bluetooth network template. 536 */ matchesBluetooth(NetworkIdentity ident)537 private boolean matchesBluetooth(NetworkIdentity ident) { 538 if (ident.mType == TYPE_BLUETOOTH) { 539 return true; 540 } 541 return false; 542 } 543 544 /** 545 * Check if matches Proxy network template. 546 */ matchesProxy(NetworkIdentity ident)547 private boolean matchesProxy(NetworkIdentity ident) { 548 return ident.mType == TYPE_PROXY; 549 } 550 getMatchRuleName(int matchRule)551 private static String getMatchRuleName(int matchRule) { 552 switch (matchRule) { 553 case MATCH_MOBILE: 554 return "MOBILE"; 555 case MATCH_WIFI: 556 return "WIFI"; 557 case MATCH_ETHERNET: 558 return "ETHERNET"; 559 case MATCH_MOBILE_WILDCARD: 560 return "MOBILE_WILDCARD"; 561 case MATCH_WIFI_WILDCARD: 562 return "WIFI_WILDCARD"; 563 case MATCH_BLUETOOTH: 564 return "BLUETOOTH"; 565 case MATCH_PROXY: 566 return "PROXY"; 567 default: 568 return "UNKNOWN(" + matchRule + ")"; 569 } 570 } 571 572 /** 573 * Examine the given template and normalize if it refers to a "merged" 574 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 575 * for key purposes, and expand the template to match all other merged 576 * subscribers. 577 * <p> 578 * For example, given an incoming template matching B, and the currently 579 * active merge set [A,B], we'd return a new template that primarily matches 580 * A, but also matches B. 581 * TODO: remove and use {@link #normalize(NetworkTemplate, List)}. 582 */ 583 @UnsupportedAppUsage normalize(NetworkTemplate template, String[] merged)584 public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) { 585 return normalize(template, Arrays.<String[]>asList(merged)); 586 } 587 588 /** 589 * Examine the given template and normalize if it refers to a "merged" 590 * mobile subscriber. We pick the "lowest" merged subscriber as the primary 591 * for key purposes, and expand the template to match all other merged 592 * subscribers. 593 * 594 * There can be multiple merged subscriberIds for multi-SIM devices. 595 * 596 * <p> 597 * For example, given an incoming template matching B, and the currently 598 * active merge set [A,B], we'd return a new template that primarily matches 599 * A, but also matches B. 600 */ normalize(NetworkTemplate template, List<String[]> mergedList)601 public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) { 602 if (!template.isMatchRuleMobile()) return template; 603 604 for (String[] merged : mergedList) { 605 if (ArrayUtils.contains(merged, template.mSubscriberId)) { 606 // Requested template subscriber is part of the merge group; return 607 // a template that matches all merged subscribers. 608 return new NetworkTemplate(template.mMatchRule, merged[0], merged, 609 template.mNetworkId); 610 } 611 } 612 613 return template; 614 } 615 616 @UnsupportedAppUsage 617 public static final @android.annotation.NonNull Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() { 618 @Override 619 public NetworkTemplate createFromParcel(Parcel in) { 620 return new NetworkTemplate(in); 621 } 622 623 @Override 624 public NetworkTemplate[] newArray(int size) { 625 return new NetworkTemplate[size]; 626 } 627 }; 628 getBytesForBackup()629 public byte[] getBytesForBackup() throws IOException { 630 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 631 DataOutputStream out = new DataOutputStream(baos); 632 633 out.writeInt(BACKUP_VERSION); 634 635 out.writeInt(mMatchRule); 636 BackupUtils.writeString(out, mSubscriberId); 637 BackupUtils.writeString(out, mNetworkId); 638 639 return baos.toByteArray(); 640 } 641 getNetworkTemplateFromBackup(DataInputStream in)642 public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in) 643 throws IOException, BackupUtils.BadVersionException { 644 int version = in.readInt(); 645 if (version < 1 || version > BACKUP_VERSION) { 646 throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); 647 } 648 649 int matchRule = in.readInt(); 650 String subscriberId = BackupUtils.readString(in); 651 String networkId = BackupUtils.readString(in); 652 653 if (!isKnownMatchRule(matchRule)) { 654 throw new BackupUtils.BadVersionException( 655 "Restored network template contains unknown match rule " + matchRule); 656 } 657 658 return new NetworkTemplate(matchRule, subscriberId, networkId); 659 } 660 } 661