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