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