content: /*
   * Copyright 2017 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.telephony;
  
  import android.annotation.CallSuper;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.text.TextUtils;
  
  import java.util.Objects;
  import java.util.UUID;
  
  /**
   * CellIdentity represents the identity of a unique cell. This is the base class for
   * CellIdentityXxx which represents cell identity for specific network access technology.
   */
  public abstract class CellIdentity implements Parcelable {
  
      /** @hide */
      public static final int INVALID_CHANNEL_NUMBER = -1;
  
      // Log tag
      /** @hide */
      protected final String mTag;
      // Cell identity type
      /** @hide */
      protected final int mType;
      // 3-digit Mobile Country Code in string format. Null for CDMA cell identity.
      /** @hide */
      protected final String mMccStr;
      // 2 or 3-digit Mobile Network Code in string format. Null for CDMA cell identity.
      /** @hide */
      protected final String mMncStr;
  
      // long alpha Operator Name String or Enhanced Operator Name String
      /** @hide */
      protected String mAlphaLong;
      // short alpha Operator Name String or Enhanced Operator Name String
      /** @hide */
      protected String mAlphaShort;
  
      /** @hide */
      protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
                             String alphas) {
          mTag = tag;
          mType = type;
  
          // Only allow INT_MAX if unknown string mcc/mnc
          if (mcc == null || isMcc(mcc)) {
              mMccStr = mcc;
          } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
              // If the mccStr is empty or unknown, set it as null.
              mMccStr = null;
          } else {
              // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
              // after the bug got fixed.
              mMccStr = null;
              log("invalid MCC format: " + mcc);
          }
  
          if (mnc == null || isMnc(mnc)) {
              mMncStr = mnc;
          } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
              // If the mncStr is empty or unknown, set it as null.
              mMncStr = null;
          } else {
              // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
              // after the bug got fixed.
              mMncStr = null;
              log("invalid MNC format: " + mnc);
          }
  
          if ((mMccStr != null && mMncStr == null) || (mMccStr == null && mMncStr != null)) {
              AnomalyReporter.reportAnomaly(
                      UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
                      "CellIdentity Missing Half of PLMN ID");
          }
  
          mAlphaLong = alphal;
          mAlphaShort = alphas;
      }
  
      /** Implement the Parcelable interface */
      @Override
      public int describeContents() {
          return 0;
      }
  
      /**
       * @hide
       * @return The type of the cell identity
       */
      public @CellInfo.Type int getType() {
          return mType;
      }
  
      /**
       * @return MCC or null for CDMA
       * @hide
       */
      public String getMccString() {
          return mMccStr;
      }
  
      /**
       * @return MNC or null for CDMA
       * @hide
       */
      public String getMncString() {
          return mMncStr;
      }
  
      /**
       * Returns the channel number of the cell identity.
       *
       * @hide
       * @return The channel number, or {@link #INVALID_CHANNEL_NUMBER} if not implemented
       */
      public int getChannelNumber() {
          return INVALID_CHANNEL_NUMBER;
      }
  
      /**
       * @return The long alpha tag associated with the current scan result (may be the operator
       * name string or extended operator name string). May be null if unknown.
       */
      @Nullable
      public CharSequence getOperatorAlphaLong() {
          return mAlphaLong;
      }
  
      /**
       * @hide
       */
      public void setOperatorAlphaLong(String alphaLong) {
          mAlphaLong = alphaLong;
      }
  
      /**
       * @return The short alpha tag associated with the current scan result (may be the operator
       * name string or extended operator name string).  May be null if unknown.
       */
      @Nullable
      public CharSequence getOperatorAlphaShort() {
          return mAlphaShort;
      }
  
      /**
       * @hide
       */
      public void setOperatorAlphaShort(String alphaShort) {
          mAlphaShort = alphaShort;
      }
  
      /**
       * @return a CellLocation object for this CellIdentity
       * @hide
       */
      public abstract CellLocation asCellLocation();
  
      @Override
      public boolean equals(Object other) {
          if (!(other instanceof CellIdentity)) {
              return false;
          }
  
          CellIdentity o = (CellIdentity) other;
          return mType == o.mType
                  && TextUtils.equals(mMccStr, o.mMccStr)
                  && TextUtils.equals(mMncStr, o.mMncStr)
                  && TextUtils.equals(mAlphaLong, o.mAlphaLong)
                  && TextUtils.equals(mAlphaShort, o.mAlphaShort);
      }
  
      @Override
      public int hashCode() {
          return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
      }
  
      /**
       * Used by child classes for parceling.
       *
       * @hide
       */
      @CallSuper
      public void writeToParcel(Parcel dest, int type) {
          dest.writeInt(type);
          dest.writeString(mMccStr);
          dest.writeString(mMncStr);
          dest.writeString(mAlphaLong);
          dest.writeString(mAlphaShort);
      }
  
      /**
       * Construct from Parcel
       * @hide
       */
      protected CellIdentity(String tag, int type, Parcel source) {
          this(tag, type, source.readString(), source.readString(),
                  source.readString(), source.readString());
      }
  
      /** Implement the Parcelable interface */
      public static final @android.annotation.NonNull Creator<CellIdentity> CREATOR =
              new Creator<CellIdentity>() {
                  @Override
                  public CellIdentity createFromParcel(Parcel in) {
                      int type = in.readInt();
                      switch (type) {
                          case CellInfo.TYPE_GSM: return CellIdentityGsm.createFromParcelBody(in);
                          case CellInfo.TYPE_WCDMA: return CellIdentityWcdma.createFromParcelBody(in);
                          case CellInfo.TYPE_CDMA: return CellIdentityCdma.createFromParcelBody(in);
                          case CellInfo.TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
                          case CellInfo.TYPE_TDSCDMA:
                              return CellIdentityTdscdma.createFromParcelBody(in);
                          case CellInfo.TYPE_NR: return CellIdentityNr.createFromParcelBody(in);
                          default: throw new IllegalArgumentException("Bad Cell identity Parcel");
                      }
                  }
  
                  @Override
                  public CellIdentity[] newArray(int size) {
                      return new CellIdentity[size];
                  }
              };
  
      /** @hide */
      protected void log(String s) {
          Rlog.w(mTag, s);
      }
  
      /** @hide */
      protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
          if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
          return value;
      }
  
      /** @hide */
      protected static final long inRangeOrUnavailable(long value, long rangeMin, long rangeMax) {
          if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE_LONG;
          return value;
      }
  
      /** @hide */
      protected static final int inRangeOrUnavailable(
              int value, int rangeMin, int rangeMax, int special) {
          if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
          return value;
      }
  
      /** @hide */
      private static boolean isMcc(@NonNull String mcc) {
          // ensure no out of bounds indexing
          if (mcc.length() != 3) return false;
  
          // Character.isDigit allows all unicode digits, not just [0-9]
          for (int i = 0; i < 3; i++) {
              if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
          }
  
          return true;
      }
  
      /** @hide */
      private static boolean isMnc(@NonNull String mnc) {
          // ensure no out of bounds indexing
          if (mnc.length() < 2 || mnc.length() > 3) return false;
  
          // Character.isDigit allows all unicode digits, not just [0-9]
          for (int i = 0; i < mnc.length(); i++) {
              if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
          }
  
          return true;
      }
  }
  