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_ETHERNET;
20import static android.net.ConnectivityManager.TYPE_WIFI;
21import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
22import static android.net.ConnectivityManager.TYPE_WIMAX;
23import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
24import static android.net.NetworkIdentity.scrubSubscriberId;
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.Objects;
39
40/**
41 * Template definition used to generically match {@link NetworkIdentity},
42 * usually when collecting statistics.
43 *
44 * @hide
45 */
46public class NetworkTemplate implements Parcelable {
47
48    public static final int MATCH_MOBILE_ALL = 1;
49    public static final int MATCH_MOBILE_3G_LOWER = 2;
50    public static final int MATCH_MOBILE_4G = 3;
51    public static final int MATCH_WIFI = 4;
52    public static final int MATCH_ETHERNET = 5;
53    public static final int MATCH_MOBILE_WILDCARD = 6;
54    public static final int MATCH_WIFI_WILDCARD = 7;
55
56    /**
57     * Set of {@link NetworkInfo#getType()} that reflect data usage.
58     */
59    private static final int[] DATA_USAGE_NETWORK_TYPES;
60
61    static {
62        DATA_USAGE_NETWORK_TYPES = Resources.getSystem().getIntArray(
63                com.android.internal.R.array.config_data_usage_network_types);
64    }
65
66    private static boolean sForceAllNetworkTypes = false;
67
68    @VisibleForTesting
69    public static void forceAllNetworkTypes() {
70        sForceAllNetworkTypes = true;
71    }
72
73    /**
74     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
75     * the given IMSI.
76     */
77    public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
78        return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null);
79    }
80
81    /**
82     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
83     * the given IMSI that roughly meet a "3G" definition, or lower.
84     */
85    @Deprecated
86    public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) {
87        return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null);
88    }
89
90    /**
91     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
92     * the given IMSI that roughly meet a "4G" definition.
93     */
94    @Deprecated
95    public static NetworkTemplate buildTemplateMobile4g(String subscriberId) {
96        return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null);
97    }
98
99    /**
100     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks,
101     * regardless of IMSI.
102     */
103    public static NetworkTemplate buildTemplateMobileWildcard() {
104        return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);
105    }
106
107    /**
108     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks,
109     * regardless of SSID.
110     */
111    public static NetworkTemplate buildTemplateWifiWildcard() {
112        return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
113    }
114
115    @Deprecated
116    public static NetworkTemplate buildTemplateWifi() {
117        return buildTemplateWifiWildcard();
118    }
119
120    /**
121     * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
122     * given SSID.
123     */
124    public static NetworkTemplate buildTemplateWifi(String networkId) {
125        return new NetworkTemplate(MATCH_WIFI, null, networkId);
126    }
127
128    /**
129     * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
130     * networks together.
131     */
132    public static NetworkTemplate buildTemplateEthernet() {
133        return new NetworkTemplate(MATCH_ETHERNET, null, null);
134    }
135
136    private final int mMatchRule;
137    private final String mSubscriberId;
138    private final String mNetworkId;
139
140    public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
141        mMatchRule = matchRule;
142        mSubscriberId = subscriberId;
143        mNetworkId = networkId;
144    }
145
146    private NetworkTemplate(Parcel in) {
147        mMatchRule = in.readInt();
148        mSubscriberId = in.readString();
149        mNetworkId = in.readString();
150    }
151
152    @Override
153    public void writeToParcel(Parcel dest, int flags) {
154        dest.writeInt(mMatchRule);
155        dest.writeString(mSubscriberId);
156        dest.writeString(mNetworkId);
157    }
158
159    @Override
160    public int describeContents() {
161        return 0;
162    }
163
164    @Override
165    public String toString() {
166        final StringBuilder builder = new StringBuilder("NetworkTemplate: ");
167        builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
168        if (mSubscriberId != null) {
169            builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
170        }
171        if (mNetworkId != null) {
172            builder.append(", networkId=").append(mNetworkId);
173        }
174        return builder.toString();
175    }
176
177    @Override
178    public int hashCode() {
179        return Objects.hashCode(mMatchRule, mSubscriberId, mNetworkId);
180    }
181
182    @Override
183    public boolean equals(Object obj) {
184        if (obj instanceof NetworkTemplate) {
185            final NetworkTemplate other = (NetworkTemplate) obj;
186            return mMatchRule == other.mMatchRule
187                    && Objects.equal(mSubscriberId, other.mSubscriberId)
188                    && Objects.equal(mNetworkId, other.mNetworkId);
189        }
190        return false;
191    }
192
193    public int getMatchRule() {
194        return mMatchRule;
195    }
196
197    public String getSubscriberId() {
198        return mSubscriberId;
199    }
200
201    public String getNetworkId() {
202        return mNetworkId;
203    }
204
205    /**
206     * Test if given {@link NetworkIdentity} matches this template.
207     */
208    public boolean matches(NetworkIdentity ident) {
209        switch (mMatchRule) {
210            case MATCH_MOBILE_ALL:
211                return matchesMobile(ident);
212            case MATCH_MOBILE_3G_LOWER:
213                return matchesMobile3gLower(ident);
214            case MATCH_MOBILE_4G:
215                return matchesMobile4g(ident);
216            case MATCH_WIFI:
217                return matchesWifi(ident);
218            case MATCH_ETHERNET:
219                return matchesEthernet(ident);
220            case MATCH_MOBILE_WILDCARD:
221                return matchesMobileWildcard(ident);
222            case MATCH_WIFI_WILDCARD:
223                return matchesWifiWildcard(ident);
224            default:
225                throw new IllegalArgumentException("unknown network template");
226        }
227    }
228
229    /**
230     * Check if mobile network with matching IMSI.
231     */
232    private boolean matchesMobile(NetworkIdentity ident) {
233        if (ident.mType == TYPE_WIMAX) {
234            // TODO: consider matching against WiMAX subscriber identity
235            return true;
236        } else {
237            return ((sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType))
238                    && Objects.equal(mSubscriberId, ident.mSubscriberId));
239        }
240    }
241
242    /**
243     * Check if mobile network classified 3G or lower with matching IMSI.
244     */
245    private boolean matchesMobile3gLower(NetworkIdentity ident) {
246        ensureSubtypeAvailable();
247        if (ident.mType == TYPE_WIMAX) {
248            return false;
249        } else if (matchesMobile(ident)) {
250            switch (getNetworkClass(ident.mSubType)) {
251                case NETWORK_CLASS_UNKNOWN:
252                case NETWORK_CLASS_2_G:
253                case NETWORK_CLASS_3_G:
254                    return true;
255            }
256        }
257        return false;
258    }
259
260    /**
261     * Check if mobile network classified 4G with matching IMSI.
262     */
263    private boolean matchesMobile4g(NetworkIdentity ident) {
264        ensureSubtypeAvailable();
265        if (ident.mType == TYPE_WIMAX) {
266            // TODO: consider matching against WiMAX subscriber identity
267            return true;
268        } else if (matchesMobile(ident)) {
269            switch (getNetworkClass(ident.mSubType)) {
270                case NETWORK_CLASS_4_G:
271                    return true;
272            }
273        }
274        return false;
275    }
276
277    /**
278     * Check if matches Wi-Fi network template.
279     */
280    private boolean matchesWifi(NetworkIdentity ident) {
281        switch (ident.mType) {
282            case TYPE_WIFI:
283                return Objects.equal(
284                        removeDoubleQuotes(mNetworkId), removeDoubleQuotes(ident.mNetworkId));
285            default:
286                return false;
287        }
288    }
289
290    /**
291     * Check if matches Ethernet network template.
292     */
293    private boolean matchesEthernet(NetworkIdentity ident) {
294        if (ident.mType == TYPE_ETHERNET) {
295            return true;
296        }
297        return false;
298    }
299
300    private boolean matchesMobileWildcard(NetworkIdentity ident) {
301        if (ident.mType == TYPE_WIMAX) {
302            return true;
303        } else {
304            return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
305        }
306    }
307
308    private boolean matchesWifiWildcard(NetworkIdentity ident) {
309        switch (ident.mType) {
310            case TYPE_WIFI:
311            case TYPE_WIFI_P2P:
312                return true;
313            default:
314                return false;
315        }
316    }
317
318    private static String getMatchRuleName(int matchRule) {
319        switch (matchRule) {
320            case MATCH_MOBILE_3G_LOWER:
321                return "MOBILE_3G_LOWER";
322            case MATCH_MOBILE_4G:
323                return "MOBILE_4G";
324            case MATCH_MOBILE_ALL:
325                return "MOBILE_ALL";
326            case MATCH_WIFI:
327                return "WIFI";
328            case MATCH_ETHERNET:
329                return "ETHERNET";
330            case MATCH_MOBILE_WILDCARD:
331                return "MOBILE_WILDCARD";
332            case MATCH_WIFI_WILDCARD:
333                return "WIFI_WILDCARD";
334            default:
335                return "UNKNOWN";
336        }
337    }
338
339    private static void ensureSubtypeAvailable() {
340        if (COMBINE_SUBTYPE_ENABLED) {
341            throw new IllegalArgumentException(
342                    "Unable to enforce 3G_LOWER template on combined data.");
343        }
344    }
345
346    public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
347        @Override
348        public NetworkTemplate createFromParcel(Parcel in) {
349            return new NetworkTemplate(in);
350        }
351
352        @Override
353        public NetworkTemplate[] newArray(int size) {
354            return new NetworkTemplate[size];
355        }
356    };
357}
358