/* * Copyright (C) 2018 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 static android.text.TextUtils.formatSimple; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.telephony.AccessNetworkConstants.NgranBands.NgranBand; import android.telephony.gsm.GsmCellLocation; import android.util.ArraySet; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Objects; import java.util.Set; /** * Information to represent a unique NR(New Radio 5G) cell. */ public final class CellIdentityNr extends CellIdentity { private static final String TAG = "CellIdentityNr"; private static final int MAX_PCI = 1007; private static final int MAX_TAC = 16777215; // 0xffffff private static final int MAX_NRARFCN = 3279165; private static final long MAX_NCI = 68719476735L; private final int mNrArfcn; private final int mPci; private final int mTac; private final long mNci; private final int[] mBands; // a list of additional PLMN-IDs reported for this cell private final ArraySet mAdditionalPlmns; /** @hide */ public CellIdentityNr() { super(TAG, CellInfo.TYPE_NR, null, null, null, null); mNrArfcn = CellInfo.UNAVAILABLE; mPci = CellInfo.UNAVAILABLE; mTac = CellInfo.UNAVAILABLE; mNci = CellInfo.UNAVAILABLE; mBands = new int[] {}; mAdditionalPlmns = new ArraySet(); mGlobalCellId = null; } /** * @param pci Physical Cell Id in range [0, 1007]. * @param tac 24-bit Tracking Area Code. * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2. * @param mccStr 3-digit Mobile Country Code in string format. * @param mncStr 2 or 3-digit Mobile Network Code in string format. * @param nci The 36-bit NR Cell Identity in range [0, 68719476735]. * @param alphal long alpha Operator Name String or Enhanced Operator Name String. * @param alphas short alpha Operator Name String or Enhanced Operator Name String. * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell * * @hide */ public CellIdentityNr(int pci, int tac, int nrArfcn, @NonNull @NgranBand int[] bands, @Nullable String mccStr, @Nullable String mncStr, long nci, @Nullable String alphal, @Nullable String alphas, @NonNull Collection additionalPlmns) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN); // TODO: input validation for bands mBands = bands; mNci = inRangeOrUnavailable(nci, 0, MAX_NCI); mAdditionalPlmns = new ArraySet<>(additionalPlmns.size()); for (String plmn : additionalPlmns) { if (isValidPlmn(plmn)) { mAdditionalPlmns.add(plmn); } } updateGlobalCellId(); } /** @hide */ @Override public @NonNull CellIdentityNr sanitizeLocationInfo() { return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn, mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE_LONG, mAlphaLong, mAlphaShort, mAdditionalPlmns); } /** @hide */ protected void updateGlobalCellId() { mGlobalCellId = null; String plmn = getPlmn(); if (plmn == null) return; if (mNci == CellInfo.UNAVAILABLE_LONG) return; mGlobalCellId = plmn + formatSimple("%09x", mNci); } /** * @return a CellLocation object for this CellIdentity. * @hide */ @NonNull @Override public CellLocation asCellLocation() { GsmCellLocation cl = new GsmCellLocation(); int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1; cl.setLacAndCid(tac, -1); cl.setPsc(0); return cl; } @Override public int hashCode() { return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, Arrays.hashCode(mBands), mNci, mAdditionalPlmns.hashCode()); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof CellIdentityNr)) { return false; } CellIdentityNr o = (CellIdentityNr) other; return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn && Arrays.equals(mBands, o.mBands) && mNci == o.mNci && mAdditionalPlmns.equals(o.mAdditionalPlmns); } /** * Get the NR(New Radio 5G) Cell Identity. * * @return The 36-bit NR Cell Identity in range [0, 68719476735] or * {@link CellInfo#UNAVAILABLE_LONG} if unknown. */ public long getNci() { return mNci; } /** * Get the New Radio Absolute Radio Frequency Channel Number. * * Reference: 3GPP TS 38.101-1 section 5.4.2.1 NR-ARFCN and channel raster. * Reference: 3GPP TS 38.101-2 section 5.4.2.1 NR-ARFCN and channel raster. * * @return Integer value in range [0, 3279165] or {@link CellInfo#UNAVAILABLE} if unknown. */ @IntRange(from = 0, to = 3279165) public int getNrarfcn() { return mNrArfcn; } /** * Get bands of the cell * * Reference: TS 38.101-1 table 5.2-1 * Reference: TS 38.101-2 table 5.2-1 * * @return Array of band number or empty array if not available. */ @NgranBand @NonNull public int[] getBands() { return Arrays.copyOf(mBands, mBands.length); } /** * Get the physical cell id. * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown. */ @IntRange(from = 0, to = 1007) public int getPci() { return mPci; } /** * Get the tracking area code. * @return a 24 bit integer or {@link CellInfo#UNAVAILABLE} if unknown. */ @IntRange(from = 0, to = 16777215) public int getTac() { return mTac; } /** * @return Mobile Country Code in string format, or {@code null} if unknown. */ @Nullable public String getMccString() { return mMccStr; } /** * @return Mobile Network Code in string format, or {@code null} if unknown. */ @Nullable public String getMncString() { return mMncStr; } /** @hide */ @Override public int getChannelNumber() { return mNrArfcn; } /** * @return a list of additional PLMN IDs supported by this cell. */ @NonNull public Set getAdditionalPlmns() { return Collections.unmodifiableSet(mAdditionalPlmns); } @Override public String toString() { return new StringBuilder(TAG + ":{") .append(" mPci = ").append(mPci) .append(" mTac = ").append(mTac) .append(" mNrArfcn = ").append(mNrArfcn) .append(" mBands = ").append(Arrays.toString(mBands)) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) .append(" mAdditionalPlmns = ").append(mAdditionalPlmns) .append(" }") .toString(); } @Override public void writeToParcel(Parcel dest, int type) { super.writeToParcel(dest, CellInfo.TYPE_NR); dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); dest.writeIntArray(mBands); dest.writeLong(mNci); dest.writeArraySet(mAdditionalPlmns); } /** Construct from Parcel, type has already been processed */ private CellIdentityNr(Parcel in) { super(TAG, CellInfo.TYPE_NR, in); mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); mBands = in.createIntArray(); mNci = in.readLong(); mAdditionalPlmns = (ArraySet) in.readArraySet(null); updateGlobalCellId(); } /** Implement the Parcelable interface */ public static final @android.annotation.NonNull Creator CREATOR = new Creator() { @Override public CellIdentityNr createFromParcel(Parcel in) { // Skip the type info. in.readInt(); return createFromParcelBody(in); } @Override public CellIdentityNr[] newArray(int size) { return new CellIdentityNr[size]; } }; /** @hide */ protected static CellIdentityNr createFromParcelBody(Parcel in) { return new CellIdentityNr(in); } }