Home | History | Annotate | Download | only in net
      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
     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      */
     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
     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
    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      */
    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      */
    117     public static NetworkTemplate buildTemplateWifiWildcard() {
    118         return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
    119     }
    120 
    121     @Deprecated
    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      */
    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      */
    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      */
    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      */
    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 
    173     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
    174         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
    175     }
    176 
    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 
    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
    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
    201     public int describeContents() {
    202         return 0;
    203     }
    204 
    205     @Override
    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
    224     public int hashCode() {
    225         return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
    226     }
    227 
    228     @Override
    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 
    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 
    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 
    261     public int getMatchRule() {
    262         return mMatchRule;
    263     }
    264 
    265     public String getSubscriberId() {
    266         return mSubscriberId;
    267     }
    268 
    269     public String getNetworkId() {
    270         return mNetworkId;
    271     }
    272 
    273     /**
    274      * Test if given {@link NetworkIdentity} matches this template.
    275      */
    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      */
    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
    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
    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      */
    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      */
    368     private boolean matchesEthernet(NetworkIdentity ident) {
    369         if (ident.mType == TYPE_ETHERNET) {
    370             return true;
    371         }
    372         return false;
    373     }
    374 
    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 
    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      */
    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      */
    406     private boolean matchesProxy(NetworkIdentity ident) {
    407         return ident.mType == TYPE_PROXY;
    408     }
    409 
    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 
    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      */
    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 
    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 
    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