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