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