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