content: /*
   * Copyright (C) 2012 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.IntRange;
  import android.annotation.UnsupportedAppUsage;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.os.PersistableBundle;
  
  import java.util.Arrays;
  import java.util.Objects;
  
  /**
   * LTE signal strength related information.
   */
  public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
  
      private static final String LOG_TAG = "CellSignalStrengthLte";
      private static final boolean DBG = false;
  
      /**
       * Indicates the unknown or undetectable RSSI value in ASU.
       *
       * Reference: TS 27.007 8.5 - Signal quality +CSQ
       */
      private static final int SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN = 99;
      /**
       * Indicates the maximum valid RSSI value in ASU.
       *
       * Reference: TS 27.007 8.5 - Signal quality +CSQ
       */
      private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE = 31;
      /**
       * Indicates the minimum valid RSSI value in ASU.
       *
       * Reference: TS 27.007 8.5 - Signal quality +CSQ
       */
      private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0;
  
      private static final int MAX_LTE_RSRP = -44;
      private static final int MIN_LTE_RSRP = -140;
  
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mSignalStrength; // To be removed
      private int mRssi;
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mRsrp;
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mRsrq;
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mRssnr;
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mCqi;
      @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
      private int mTimingAdvance;
      private int mLevel;
  
      /** @hide */
      @UnsupportedAppUsage
      public CellSignalStrengthLte() {
          setDefaultValues();
      }
  
      /**
       * Construct a cell signal strength
       *
       * @param rssi in dBm [-113,-51], UNKNOWN
       * @param rsrp in dBm [-140,-43], UNKNOWN
       * @param rsrq in dB [-20,-3], UNKNOWN
       * @param rssnr in 10*dB [-200, +300], UNKNOWN
       * @param cqi [0, 15], UNKNOWN
       * @param timingAdvance [0, 1282], UNKNOWN
       *
       */
      /** @hide */
      public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
              int timingAdvance) {
  
          mRssi = inRangeOrUnavailable(rssi, -113, -51);
          mSignalStrength = mRssi;
          mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
          mRsrq = inRangeOrUnavailable(rsrq, -20, -3);
          mRssnr = inRangeOrUnavailable(rssnr, -200, 300);
          mCqi = inRangeOrUnavailable(cqi, 0, 15);
          mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
          updateLevel(null, null);
      }
  
      /** @hide */
      public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) {
          // Convert from HAL values as part of construction.
          this(convertRssiAsuToDBm(lte.signalStrength),
                  lte.rsrp != CellInfo.UNAVAILABLE ? -lte.rsrp : lte.rsrp,
                  lte.rsrq != CellInfo.UNAVAILABLE ? -lte.rsrq : lte.rsrq,
                  lte.rssnr, lte.cqi, lte.timingAdvance);
      }
  
      /** @hide */
      public CellSignalStrengthLte(CellSignalStrengthLte s) {
          copyFrom(s);
      }
  
      /** @hide */
      protected void copyFrom(CellSignalStrengthLte s) {
          mSignalStrength = s.mSignalStrength;
          mRssi = s.mRssi;
          mRsrp = s.mRsrp;
          mRsrq = s.mRsrq;
          mRssnr = s.mRssnr;
          mCqi = s.mCqi;
          mTimingAdvance = s.mTimingAdvance;
          mLevel = s.mLevel;
      }
  
      /** @hide */
      @Override
      public CellSignalStrengthLte copy() {
          return new CellSignalStrengthLte(this);
      }
  
      /** @hide */
      @Override
      public void setDefaultValues() {
          mSignalStrength = CellInfo.UNAVAILABLE;
          mRssi = CellInfo.UNAVAILABLE;
          mRsrp = CellInfo.UNAVAILABLE;
          mRsrq = CellInfo.UNAVAILABLE;
          mRssnr = CellInfo.UNAVAILABLE;
          mCqi = CellInfo.UNAVAILABLE;
          mTimingAdvance = CellInfo.UNAVAILABLE;
          mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
      }
  
      /** {@inheritDoc} */
      @Override
      @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
      public int getLevel() {
          return mLevel;
      }
  
      // Lifted from Default carrier configs and max range of RSRP
      private static final int[] sThresholds = new int[]{-115, -105, -95, -85};
      private static final int sRsrpBoost = 0;
  
      /** @hide */
      @Override
      public void updateLevel(PersistableBundle cc, ServiceState ss) {
          int[] thresholds;
          boolean rsrpOnly;
          if (cc == null) {
              thresholds = sThresholds;
              rsrpOnly = false;
          } else {
              rsrpOnly = cc.getBoolean(
                      CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
              thresholds = cc.getIntArray(
                      CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
              if (thresholds == null) thresholds = sThresholds;
              if (DBG) log("updateLevel() carrierconfig - rsrpOnly="
                      + rsrpOnly + ", thresholds=" + Arrays.toString(thresholds));
          }
  
  
          int rsrpBoost = 0;
          if (ss != null) {
              rsrpBoost = ss.getLteEarfcnRsrpBoost();
          }
  
          int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
          int rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
          int snrIconLevel = -1;
  
          int rsrp = mRsrp + rsrpBoost;
  
          if (rsrp < MIN_LTE_RSRP || rsrp > MAX_LTE_RSRP) {
              rsrpIconLevel = -1;
          } else {
              rsrpIconLevel = thresholds.length;
              while (rsrpIconLevel > 0 && rsrp < thresholds[rsrpIconLevel - 1]) rsrpIconLevel--;
          }
  
          if (rsrpOnly) {
              if (DBG) log("updateLevel() - rsrp = " + rsrpIconLevel);
              if (rsrpIconLevel != -1) {
                  mLevel = rsrpIconLevel;
                  return;
              }
          }
  
          /*
           * Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
           * dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
           * -3.0 dB <= RS_SNR < 1.0 dB 1 bar RS_SNR < -3.0 dB/No Service Antenna
           * Icon Only
           */
          if (mRssnr > 300) snrIconLevel = -1;
          else if (mRssnr >= 130) snrIconLevel = SIGNAL_STRENGTH_GREAT;
          else if (mRssnr >= 45) snrIconLevel = SIGNAL_STRENGTH_GOOD;
          else if (mRssnr >= 10) snrIconLevel = SIGNAL_STRENGTH_MODERATE;
          else if (mRssnr >= -30) snrIconLevel = SIGNAL_STRENGTH_POOR;
          else if (mRssnr >= -200)
              snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
  
          if (DBG) log("updateLevel() - rsrp:" + mRsrp + " snr:" + mRssnr + " rsrpIconLevel:"
                  + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
                  + " lteRsrpBoost:" + sRsrpBoost);
  
          /* Choose a measurement type to use for notification */
          if (snrIconLevel != -1 && rsrpIconLevel != -1) {
              /*
               * The number of bars displayed shall be the smaller of the bars
               * associated with LTE RSRP and the bars associated with the LTE
               * RS_SNR
               */
              mLevel = (rsrpIconLevel < snrIconLevel ? rsrpIconLevel : snrIconLevel);
              return;
          }
  
          if (snrIconLevel != -1) {
              mLevel = snrIconLevel;
              return;
          }
  
          if (rsrpIconLevel != -1) {
              mLevel = rsrpIconLevel;
              return;
          }
  
          if (mRssi > -51) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
          else if (mRssi >= -89) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
          else if (mRssi >= -97) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
          else if (mRssi >= -103) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
          else if (mRssi >= -113) rssiIconLevel = SIGNAL_STRENGTH_POOR;
          else rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
          if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:"
                  + rssiIconLevel);
          mLevel = rssiIconLevel;
      }
  
      /**
       * Get reference signal received quality
       *
       * @return the RSRQ if available or
       *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
       */
      public int getRsrq() {
          return mRsrq;
      }
  
      /**
       * Get Received Signal Strength Indication (RSSI) in dBm
       *
       * The value range is [-113, -51] inclusively or {@link CellInfo#UNAVAILABLE} if unavailable.
       *
       * Reference: TS 27.007 8.5 Signal quality +CSQ
       *
       * @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable.
       */
      public int getRssi() {
          return mRssi;
      }
  
      /**
       * Get reference signal signal-to-noise ratio
       *
       * @return the RSSNR if available or
       *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
       */
      public int getRssnr() {
          return mRssnr;
      }
  
      /**
       * Get reference signal received power in dBm
       *
       * @return the RSRP of the measured cell.
       */
      public int getRsrp() {
          return mRsrp;
      }
  
      /**
       * Get channel quality indicator
       *
       * @return the CQI if available or
       *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
       */
      public int getCqi() {
          return mCqi;
      }
  
      /**
       * Get signal strength in dBm
       *
       * @return the RSRP of the measured cell.
       */
      @Override
      public int getDbm() {
          return mRsrp;
      }
  
      /**
       * Get the RSRP in ASU.
       *
       * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
       *
       * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
       */
      @Override
      public int getAsuLevel() {
          int lteAsuLevel = 99;
          int lteDbm = mRsrp;
          if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
          else if (lteDbm <= -140) lteAsuLevel = 0;
          else if (lteDbm >= -43) lteAsuLevel = 97;
          else lteAsuLevel = lteDbm + 140;
          if (DBG) log("Lte Asu level: "+lteAsuLevel);
          return lteAsuLevel;
      }
  
      /**
       * Get the timing advance value for LTE, as a value in range of 0..1282.
       * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no
       * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3
       *
       * @return the LTE timing advance if available or
       *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
       */
      public int getTimingAdvance() {
          return mTimingAdvance;
      }
  
      @Override
      public int hashCode() {
          return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel);
      }
  
      private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte();
  
      /** @hide */
      @Override
      public boolean isValid() {
          return !this.equals(sInvalid);
      }
  
      @Override
      public boolean equals (Object o) {
          CellSignalStrengthLte s;
  
          if (!(o instanceof CellSignalStrengthLte)) return false;
          s = (CellSignalStrengthLte) o;
  
          return mRssi == s.mRssi
                  && mRsrp == s.mRsrp
                  && mRsrq == s.mRsrq
                  && mRssnr == s.mRssnr
                  && mCqi == s.mCqi
                  && mTimingAdvance == s.mTimingAdvance
                  && mLevel == s.mLevel;
      }
  
      /**
       * @return string representation.
       */
      @Override
      public String toString() {
          return "CellSignalStrengthLte:"
                  + " rssi=" + mRssi
                  + " rsrp=" + mRsrp
                  + " rsrq=" + mRsrq
                  + " rssnr=" + mRssnr
                  + " cqi=" + mCqi
                  + " ta=" + mTimingAdvance
                  + " level=" + mLevel;
      }
  
      /** Implement the Parcelable interface */
      @Override
      public void writeToParcel(Parcel dest, int flags) {
          if (DBG) log("writeToParcel(Parcel, int): " + toString());
          dest.writeInt(mRssi);
          // Need to multiply rsrp and rsrq by -1
          // to ensure consistency when reading values written here
          // unless the values are invalid
          dest.writeInt(mRsrp);
          dest.writeInt(mRsrq);
          dest.writeInt(mRssnr);
          dest.writeInt(mCqi);
          dest.writeInt(mTimingAdvance);
          dest.writeInt(mLevel);
      }
  
      /**
       * Construct a SignalStrength object from the given parcel
       * where the token is already been processed.
       */
      private CellSignalStrengthLte(Parcel in) {
          mRssi = in.readInt();
          mSignalStrength = mRssi;
          mRsrp = in.readInt();
          mRsrq = in.readInt();
          mRssnr = in.readInt();
          mCqi = in.readInt();
          mTimingAdvance = in.readInt();
          mLevel = in.readInt();
          if (DBG) log("CellSignalStrengthLte(Parcel): " + toString());
      }
  
      /** Implement the Parcelable interface */
      @Override
      public int describeContents() {
          return 0;
      }
  
      /** Implement the Parcelable interface */
      @SuppressWarnings("hiding")
      public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthLte> CREATOR =
              new Parcelable.Creator<CellSignalStrengthLte>() {
          @Override
          public CellSignalStrengthLte createFromParcel(Parcel in) {
              return new CellSignalStrengthLte(in);
          }
  
          @Override
          public CellSignalStrengthLte[] newArray(int size) {
              return new CellSignalStrengthLte[size];
          }
      };
  
      /**
       * log
       */
      private static void log(String s) {
          Rlog.w(LOG_TAG, s);
      }
  
      private static int convertRssiAsuToDBm(int rssiAsu) {
          if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
              return CellInfo.UNAVAILABLE;
          }
          if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
                  || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
              Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
              return CellInfo.UNAVAILABLE;
          }
          return -113 + (2 * rssiAsu);
      }
  }
  