/* * 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.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import com.android.telephony.Rlog; 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 = Integer.MAX_VALUE; /** * parameters for validation * @hide */ public static final int MCC_LENGTH = 3; /** @hide */ public static final int MNC_MIN_LENGTH = 2; /** @hide */ public static final int MNC_MAX_LENGTH = 3; // 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; // Cell Global, 3GPP TS 23.003 /** @hide */ protected String mGlobalCellId; /** @hide */ protected CellIdentity(@Nullable String tag, int type, @Nullable String mcc, @Nullable String mnc, @Nullable String alphal, @Nullable 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("e257ae06-ac0a-44c0-ba63-823b9f07b3e4"), "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 Global Cell ID * @hide */ @Nullable public String getGlobalCellId() { return mGlobalCellId; } /** * @param ci a CellIdentity to compare to the current CellIdentity. * @return true if ci has the same technology and Global Cell ID; false, otherwise. * @hide */ public boolean isSameCell(@Nullable CellIdentity ci) { if (ci == null) return false; if (this.getClass() != ci.getClass()) return false; return TextUtils.equals(this.getGlobalCellId(), ci.getGlobalCellId()); } /** @hide */ public @Nullable String getPlmn() { if (mMccStr == null || mMncStr == null) return null; return mMccStr + mMncStr; } /** @hide */ protected abstract void updateGlobalCellId(); /** * @return a CellLocation object for this CellIdentity * @hide */ @SystemApi public abstract @NonNull CellLocation asCellLocation(); /** * Create and a return a new instance of CellIdentity with location-identifying information * removed. * * @hide */ @SystemApi public abstract @NonNull CellIdentity sanitizeLocationInfo(); @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); } /** Used by phone interface manager to verify if a given string is valid MccMnc * @hide */ public static boolean isValidPlmn(@NonNull String plmn) { if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) { return false; } return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH))); } /** * 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 CREATOR = new Creator() { @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() != MCC_LENGTH) return false; // Character.isDigit allows all unicode digits, not just [0-9] for (int i = 0; i < MCC_LENGTH; 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() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) 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; } }