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_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 
     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 
     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      */
     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      */
    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     /**
    149      * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
    150      * networks together.
    151      */
    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 
    176     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
    177         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
    178     }
    179 
    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 
    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 
    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
    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
    224     public int describeContents() {
    225         return 0;
    226     }
    227 
    228     @Override
    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
    257     public int hashCode() {
    258         return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
    259                 mDefaultNetwork);
    260     }
    261 
    262     @Override
    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 
    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 
    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 
    296     public int getMatchRule() {
    297         return mMatchRule;
    298     }
    299 
    300     public String getSubscriberId() {
    301         return mSubscriberId;
    302     }
    303 
    304     public String getNetworkId() {
    305         return mNetworkId;
    306     }
    307 
    308     /**
    309      * Test if given {@link NetworkIdentity} matches this template.
    310      */
    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 
    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 
    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 
    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 
    356     public boolean matchesSubscriberId(String subscriberId) {
    357         return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
    358     }
    359 
    360     /**
    361      * Check if mobile network with matching IMSI.
    362      */
    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      */
    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      */
    390     private boolean matchesEthernet(NetworkIdentity ident) {
    391         if (ident.mType == TYPE_ETHERNET) {
    392             return true;
    393         }
    394         return false;
    395     }
    396 
    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 
    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      */
    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      */
    428     private boolean matchesProxy(NetworkIdentity ident) {
    429         return ident.mType == TYPE_PROXY;
    430     }
    431 
    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      */
    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 
    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 
    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