content: /*
   * Copyright (C) 2011 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.net;
  
  import android.os.Parcel;
  import android.os.Parcelable;
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.util.Objects;
  
  import static android.net.ConnectivityManager.*;
  import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
  import static android.net.NetworkIdentity.scrubSubscriberId;
  import static com.android.internal.util.ArrayUtils.contains;
  
  /**
   * Template definition used to generically match {@link NetworkIdentity},
   * usually when collecting statistics.
   *
   * @hide
   */
  public class NetworkTemplate implements Parcelable {
  
      public static final int MATCH_MOBILE_ALL = 1;
      public static final int MATCH_MOBILE_3G_LOWER = 2;
      public static final int MATCH_MOBILE_4G = 3;
      public static final int MATCH_WIFI = 4;
      public static final int MATCH_ETHERNET = 5;
      public static final int MATCH_MOBILE_WILDCARD = 6;
      public static final int MATCH_WIFI_WILDCARD = 7;
  
      /**
       * Network type is unknown
       */
      public static final int NETWORK_TYPE_UNKNOWN = 0;
      /**
       * Current network is GPRS
       */
      public static final int NETWORK_TYPE_GPRS = 1;
      /**
       * Current network is EDGE
       */
      public static final int NETWORK_TYPE_EDGE = 2;
      /**
       * Current network is UMTS
       */
      public static final int NETWORK_TYPE_UMTS = 3;
      /**
       * Current network is CDMA: Either IS95A or IS95B
       */
      public static final int NETWORK_TYPE_CDMA = 4;
      /**
       * Current network is EVDO revision 0
       */
      public static final int NETWORK_TYPE_EVDO_0 = 5;
      /**
       * Current network is EVDO revision A
       */
      public static final int NETWORK_TYPE_EVDO_A = 6;
      /**
       * Current network is 1xRTT
       */
      public static final int NETWORK_TYPE_1xRTT = 7;
      /**
       * Current network is HSDPA
       */
      public static final int NETWORK_TYPE_HSDPA = 8;
      /**
       * Current network is HSUPA
       */
      public static final int NETWORK_TYPE_HSUPA = 9;
      /**
       * Current network is HSPA
       */
      public static final int NETWORK_TYPE_HSPA = 10;
      /**
       * Current network is iDen
       */
      public static final int NETWORK_TYPE_IDEN = 11;
      /**
       * Current network is EVDO revision B
       */
      public static final int NETWORK_TYPE_EVDO_B = 12;
      /**
       * Current network is LTE
       */
      public static final int NETWORK_TYPE_LTE = 13;
      /**
       * Current network is eHRPD
       */
      public static final int NETWORK_TYPE_EHRPD = 14;
      /**
       * Current network is HSPA+
       */
      public static final int NETWORK_TYPE_HSPAP = 15;
  
      /**
       * Unknown network class. {@hide}
       */
      public static final int NETWORK_CLASS_UNKNOWN = 0;
      /**
       * Class of broadly defined "2G" networks. {@hide}
       */
      public static final int NETWORK_CLASS_2_G = 1;
      /**
       * Class of broadly defined "3G" networks. {@hide}
       */
      public static final int NETWORK_CLASS_3_G = 2;
      /**
       * Class of broadly defined "4G" networks. {@hide}
       */
      public static final int NETWORK_CLASS_4_G = 3;
  
      /**
       * Return general class of network type, such as "3G" or "4G". In cases
       * where classification is contentious, this method is conservative.
       *
       * @hide
       */
      public static int getNetworkClass(int networkType) {
          switch (networkType) {
              case NETWORK_TYPE_GPRS:
              case NETWORK_TYPE_EDGE:
              case NETWORK_TYPE_CDMA:
              case NETWORK_TYPE_1xRTT:
              case NETWORK_TYPE_IDEN:
                  return NETWORK_CLASS_2_G;
              case NETWORK_TYPE_UMTS:
              case NETWORK_TYPE_EVDO_0:
              case NETWORK_TYPE_EVDO_A:
              case NETWORK_TYPE_HSDPA:
              case NETWORK_TYPE_HSUPA:
              case NETWORK_TYPE_HSPA:
              case NETWORK_TYPE_EVDO_B:
              case NETWORK_TYPE_EHRPD:
              case NETWORK_TYPE_HSPAP:
                  return NETWORK_CLASS_3_G;
              case NETWORK_TYPE_LTE:
                  return NETWORK_CLASS_4_G;
              default:
                  return NETWORK_CLASS_UNKNOWN;
          }
      }
  
      /**
       * Set of {@link NetworkInfo#getType()} that reflect data usage.
       */
      private static final int[] DATA_USAGE_NETWORK_TYPES = {0};
  
      private static boolean sForceAllNetworkTypes = false;
  
      @VisibleForTesting
      public static void forceAllNetworkTypes() {
          sForceAllNetworkTypes = true;
      }
  
      /**
       * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
       * the given IMSI.
       */
      public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
          return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null);
      }
  
      /**
       * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
       * the given IMSI that roughly meet a "3G" definition, or lower.
       */
      @Deprecated
      public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) {
          return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null);
      }
  
      /**
       * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
       * the given IMSI that roughly meet a "4G" definition.
       */
      @Deprecated
      public static NetworkTemplate buildTemplateMobile4g(String subscriberId) {
          return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null);
      }
  
      /**
       * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks,
       * regardless of IMSI.
       */
      public static NetworkTemplate buildTemplateMobileWildcard() {
          return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null);
      }
  
      /**
       * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks,
       * regardless of SSID.
       */
      public static NetworkTemplate buildTemplateWifiWildcard() {
          return new NetworkTemplate(MATCH_WIFI_WILDCARD, null, null);
      }
  
      @Deprecated
      public static NetworkTemplate buildTemplateWifi() {
          return buildTemplateWifiWildcard();
      }
  
      /**
       * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
       * given SSID.
       */
      public static NetworkTemplate buildTemplateWifi(String networkId) {
          return new NetworkTemplate(MATCH_WIFI, null, networkId);
      }
  
      /**
       * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
       * networks together.
       */
      public static NetworkTemplate buildTemplateEthernet() {
          return new NetworkTemplate(MATCH_ETHERNET, null, null);
      }
  
      private final int mMatchRule;
      private final String mSubscriberId;
      private final String mNetworkId;
  
      public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
          mMatchRule = matchRule;
          mSubscriberId = subscriberId;
          mNetworkId = networkId;
      }
  
      private NetworkTemplate(Parcel in) {
          mMatchRule = in.readInt();
          mSubscriberId = in.readString();
          mNetworkId = in.readString();
      }
  
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          dest.writeInt(mMatchRule);
          dest.writeString(mSubscriberId);
          dest.writeString(mNetworkId);
      }
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public String toString() {
          final StringBuilder builder = new StringBuilder("NetworkTemplate: ");
          builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
          if (mSubscriberId != null) {
              builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
          }
          if (mNetworkId != null) {
              builder.append(", networkId=").append(mNetworkId);
          }
          return builder.toString();
      }
  
      @Override
      public int hashCode() {
          return Objects.hashCode(mMatchRule, mSubscriberId, mNetworkId);
      }
  
      @Override
      public boolean equals(Object obj) {
          if (obj instanceof NetworkTemplate) {
              final NetworkTemplate other = (NetworkTemplate) obj;
              return mMatchRule == other.mMatchRule
                      && Objects.equal(mSubscriberId, other.mSubscriberId)
                      && Objects.equal(mNetworkId, other.mNetworkId);
          }
          return false;
      }
  
      public int getMatchRule() {
          return mMatchRule;
      }
  
      public String getSubscriberId() {
          return mSubscriberId;
      }
  
      public String getNetworkId() {
          return mNetworkId;
      }
  
      /**
       * Test if given {@link NetworkIdentity} matches this template.
       */
      public boolean matches(NetworkIdentity ident) {
          switch (mMatchRule) {
              case MATCH_MOBILE_ALL:
                  return matchesMobile(ident);
              case MATCH_MOBILE_3G_LOWER:
                  return matchesMobile3gLower(ident);
              case MATCH_MOBILE_4G:
                  return matchesMobile4g(ident);
              case MATCH_WIFI:
                  return matchesWifi(ident);
              case MATCH_ETHERNET:
                  return matchesEthernet(ident);
              case MATCH_MOBILE_WILDCARD:
                  return matchesMobileWildcard(ident);
              case MATCH_WIFI_WILDCARD:
                  return matchesWifiWildcard(ident);
              default:
                  throw new IllegalArgumentException("unknown network template");
          }
      }
  
      /**
       * Check if mobile network with matching IMSI.
       */
      private boolean matchesMobile(NetworkIdentity ident) {
          if (ident.mType == TYPE_WIMAX) {
              // TODO: consider matching against WiMAX subscriber identity
              return true;
          } else {
              return ((sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType))
                      && Objects.equal(mSubscriberId, ident.mSubscriberId));
          }
      }
  
      /**
       * Check if mobile network classified 3G or lower with matching IMSI.
       */
      private boolean matchesMobile3gLower(NetworkIdentity ident) {
          ensureSubtypeAvailable();
          if (ident.mType == TYPE_WIMAX) {
              return false;
          } else if (matchesMobile(ident)) {
              switch (getNetworkClass(ident.mSubType)) {
                  case 0:
                  case 1:
                  case 2:
                      return true;
              }
          }
          return false;
      }
  
      /**
       * Check if mobile network classified 4G with matching IMSI.
       */
      private boolean matchesMobile4g(NetworkIdentity ident) {
          ensureSubtypeAvailable();
          if (ident.mType == TYPE_WIMAX) {
              // TODO: consider matching against WiMAX subscriber identity
              return true;
          } else if (matchesMobile(ident)) {
              switch (getNetworkClass(ident.mSubType)) {
                  case 3:
                      return true;
              }
          }
          return false;
      }
  
      /**
       * Check if matches Wi-Fi network template.
       */
      private boolean matchesWifi(NetworkIdentity ident) {
          switch (ident.mType) {
              case TYPE_WIFI:
                  return Objects.equal(mNetworkId, ident.mNetworkId);
              default:
                  return false;
          }
      }
  
      /**
       * Check if matches Ethernet network template.
       */
      private boolean matchesEthernet(NetworkIdentity ident) {
          if (ident.mType == TYPE_ETHERNET) {
              return true;
          }
          return false;
      }
  
      private boolean matchesMobileWildcard(NetworkIdentity ident) {
          if (ident.mType == TYPE_WIMAX) {
              return true;
          } else {
              return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
          }
      }
  
      private boolean matchesWifiWildcard(NetworkIdentity ident) {
          switch (ident.mType) {
              case TYPE_WIFI:
              case 13:
                  return true;
              default:
                  return false;
          }
      }
  
      private static String getMatchRuleName(int matchRule) {
          switch (matchRule) {
              case MATCH_MOBILE_3G_LOWER:
                  return "MOBILE_3G_LOWER";
              case MATCH_MOBILE_4G:
                  return "MOBILE_4G";
              case MATCH_MOBILE_ALL:
                  return "MOBILE_ALL";
              case MATCH_WIFI:
                  return "WIFI";
              case MATCH_ETHERNET:
                  return "ETHERNET";
              case MATCH_MOBILE_WILDCARD:
                  return "MOBILE_WILDCARD";
              case MATCH_WIFI_WILDCARD:
                  return "WIFI_WILDCARD";
              default:
                  return "UNKNOWN";
          }
      }
  
      private static void ensureSubtypeAvailable() {
          if (COMBINE_SUBTYPE_ENABLED) {
              throw new IllegalArgumentException(
                      "Unable to enforce 3G_LOWER template on combined data.");
          }
      }
  
      public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
          @Override
          public NetworkTemplate createFromParcel(Parcel in) {
              return new NetworkTemplate(in);
          }
  
          @Override
          public NetworkTemplate[] newArray(int size) {
              return new NetworkTemplate[size];
          }
      };
  }
  