| /* | |
| * 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 precise location-identifying | |
| * information removed. | |
| * | |
| * Coarse-level location information that will be retained: | |
| * <p><ul> | |
| * <li> Country code (MCC) | |
| * <li> Network Identifier / Network Code (MNC) | |
| * <li> Band Information | |
| * <li> Network Alpha Tag (long, short) | |
| * </ul><p> | |
| * | |
| * Fine-level information that will be redacted: | |
| * <p><ul> | |
| * <li> Unique cell ID (Cell-ID, CGI, ECGI) | |
| * <li> Tracking area information (RAI, LAI, TAI) | |
| * <li> Channel information (ARFCN, UARFCN, EARFCN, NRARFCN) | |
| * <li> Physical cell ID (BSIC, PCI) | |
| * </ul><p> | |
| * | |
| * @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<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() != 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; | |
| } | |
| } |