1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony;
18 
19 import static android.text.TextUtils.formatSimple;
20 
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.os.Parcel;
25 import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
26 import android.telephony.gsm.GsmCellLocation;
27 import android.util.ArraySet;
28 
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Objects;
33 import java.util.Set;
34 
35 /**
36  * Information to represent a unique NR(New Radio 5G) cell.
37  */
38 public final class CellIdentityNr extends CellIdentity {
39     private static final String TAG = "CellIdentityNr";
40 
41     private static final int MAX_PCI = 1007;
42     private static final int MAX_TAC = 16777215; // 0xffffff
43     private static final int MAX_NRARFCN = 3279165;
44     private static final long MAX_NCI = 68719476735L;
45 
46     private final int mNrArfcn;
47     private final int mPci;
48     private final int mTac;
49     private final long mNci;
50     private final int[] mBands;
51 
52     // a list of additional PLMN-IDs reported for this cell
53     private final ArraySet<String> mAdditionalPlmns;
54 
55     /** @hide */
CellIdentityNr()56     public CellIdentityNr() {
57         super(TAG, CellInfo.TYPE_NR, null, null, null, null);
58         mNrArfcn = CellInfo.UNAVAILABLE;
59         mPci = CellInfo.UNAVAILABLE;
60         mTac = CellInfo.UNAVAILABLE;
61         mNci = CellInfo.UNAVAILABLE;
62         mBands = new int[] {};
63         mAdditionalPlmns = new ArraySet();
64         mGlobalCellId = null;
65     }
66 
67     /**
68      * @param pci Physical Cell Id in range [0, 1007].
69      * @param tac 24-bit Tracking Area Code.
70      * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
71      * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2.
72      * @param mccStr 3-digit Mobile Country Code in string format.
73      * @param mncStr 2 or 3-digit Mobile Network Code in string format.
74      * @param nci The 36-bit NR Cell Identity in range [0, 68719476735].
75      * @param alphal long alpha Operator Name String or Enhanced Operator Name String.
76      * @param alphas short alpha Operator Name String or Enhanced Operator Name String.
77      * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
78      *
79      * @hide
80      */
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<String> additionalPlmns)81     public CellIdentityNr(int pci, int tac, int nrArfcn, @NonNull @NgranBand int[] bands,
82                           @Nullable String mccStr, @Nullable String mncStr, long nci,
83                           @Nullable String alphal, @Nullable String alphas,
84                           @NonNull Collection<String> additionalPlmns) {
85         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
86         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
87         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
88         mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
89         // TODO: input validation for bands
90         mBands = bands;
91         mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
92         mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
93         for (String plmn : additionalPlmns) {
94             if (isValidPlmn(plmn)) {
95                 mAdditionalPlmns.add(plmn);
96             }
97         }
98         updateGlobalCellId();
99     }
100 
101     /** @hide */
102     @Override
sanitizeLocationInfo()103     public @NonNull CellIdentityNr sanitizeLocationInfo() {
104         return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn,
105                 mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE_LONG, mAlphaLong, mAlphaShort,
106                 mAdditionalPlmns);
107     }
108 
109     /** @hide */
updateGlobalCellId()110     protected void updateGlobalCellId() {
111         mGlobalCellId = null;
112         String plmn = getPlmn();
113         if (plmn == null) return;
114 
115         if (mNci == CellInfo.UNAVAILABLE_LONG) return;
116 
117         mGlobalCellId = plmn + formatSimple("%09x", mNci);
118     }
119 
120     /**
121      * @return a CellLocation object for this CellIdentity.
122      * @hide
123      */
124     @NonNull
125     @Override
asCellLocation()126     public CellLocation asCellLocation() {
127         GsmCellLocation cl = new GsmCellLocation();
128         int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
129         cl.setLacAndCid(tac, -1);
130         cl.setPsc(0);
131         return cl;
132     }
133 
134     @Override
hashCode()135     public int hashCode() {
136         return Objects.hash(super.hashCode(), mPci, mTac,
137                 mNrArfcn, Arrays.hashCode(mBands), mNci, mAdditionalPlmns.hashCode());
138     }
139 
140     @Override
equals(Object other)141     public boolean equals(Object other) {
142         if (this == other) {
143             return true;
144         }
145 
146         if (!(other instanceof CellIdentityNr)) {
147             return false;
148         }
149 
150         CellIdentityNr o = (CellIdentityNr) other;
151         return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
152                 && Arrays.equals(mBands, o.mBands) && mNci == o.mNci
153                 && mAdditionalPlmns.equals(o.mAdditionalPlmns);
154     }
155 
156     /**
157      * Get the NR(New Radio 5G) Cell Identity.
158      *
159      * @return The 36-bit NR Cell Identity in range [0, 68719476735] or
160      *         {@link CellInfo#UNAVAILABLE_LONG} if unknown.
161      */
getNci()162     public long getNci() {
163         return mNci;
164     }
165 
166     /**
167      * Get the New Radio Absolute Radio Frequency Channel Number.
168      *
169      * Reference: 3GPP TS 38.101-1 section 5.4.2.1 NR-ARFCN and channel raster.
170      * Reference: 3GPP TS 38.101-2 section 5.4.2.1 NR-ARFCN and channel raster.
171      *
172      * @return Integer value in range [0, 3279165] or {@link CellInfo#UNAVAILABLE} if unknown.
173      */
174     @IntRange(from = 0, to = 3279165)
getNrarfcn()175     public int getNrarfcn() {
176         return mNrArfcn;
177     }
178 
179     /**
180      * Get bands of the cell
181      *
182      * Reference: TS 38.101-1 table 5.2-1
183      * Reference: TS 38.101-2 table 5.2-1
184      *
185      * @return Array of band number or empty array if not available.
186      */
187     @NgranBand
188     @NonNull
getBands()189     public int[] getBands() {
190         return Arrays.copyOf(mBands, mBands.length);
191     }
192 
193     /**
194      * Get the physical cell id.
195      * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown.
196      */
197     @IntRange(from = 0, to = 1007)
getPci()198     public int getPci() {
199         return mPci;
200     }
201 
202     /**
203      * Get the tracking area code.
204      * @return a 24 bit integer or {@link CellInfo#UNAVAILABLE} if unknown.
205      */
206     @IntRange(from = 0, to = 16777215)
getTac()207     public int getTac() {
208         return mTac;
209     }
210 
211     /**
212      * @return Mobile Country Code in string format, or {@code null} if unknown.
213      */
214     @Nullable
getMccString()215     public String getMccString() {
216         return mMccStr;
217     }
218 
219     /**
220      * @return Mobile Network Code in string format, or {@code null} if unknown.
221      */
222     @Nullable
getMncString()223     public String getMncString() {
224         return mMncStr;
225     }
226 
227     /** @hide */
228     @Override
getChannelNumber()229     public int getChannelNumber() {
230         return mNrArfcn;
231     }
232 
233     /**
234      * @return a list of additional PLMN IDs supported by this cell.
235      */
236     @NonNull
getAdditionalPlmns()237     public Set<String> getAdditionalPlmns() {
238         return Collections.unmodifiableSet(mAdditionalPlmns);
239     }
240 
241     @Override
toString()242     public String toString() {
243         return new StringBuilder(TAG + ":{")
244                 .append(" mPci = ").append(mPci)
245                 .append(" mTac = ").append(mTac)
246                 .append(" mNrArfcn = ").append(mNrArfcn)
247                 .append(" mBands = ").append(Arrays.toString(mBands))
248                 .append(" mMcc = ").append(mMccStr)
249                 .append(" mMnc = ").append(mMncStr)
250                 .append(" mNci = ").append(mNci)
251                 .append(" mAlphaLong = ").append(mAlphaLong)
252                 .append(" mAlphaShort = ").append(mAlphaShort)
253                 .append(" mAdditionalPlmns = ").append(mAdditionalPlmns)
254                 .append(" }")
255                 .toString();
256     }
257 
258     @Override
writeToParcel(Parcel dest, int type)259     public void writeToParcel(Parcel dest, int type) {
260         super.writeToParcel(dest, CellInfo.TYPE_NR);
261         dest.writeInt(mPci);
262         dest.writeInt(mTac);
263         dest.writeInt(mNrArfcn);
264         dest.writeIntArray(mBands);
265         dest.writeLong(mNci);
266         dest.writeArraySet(mAdditionalPlmns);
267     }
268 
269     /** Construct from Parcel, type has already been processed */
CellIdentityNr(Parcel in)270     private CellIdentityNr(Parcel in) {
271         super(TAG, CellInfo.TYPE_NR, in);
272         mPci = in.readInt();
273         mTac = in.readInt();
274         mNrArfcn = in.readInt();
275         mBands = in.createIntArray();
276         mNci = in.readLong();
277         mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
278 
279         updateGlobalCellId();
280     }
281 
282     /** Implement the Parcelable interface */
283     public static final @android.annotation.NonNull Creator<CellIdentityNr> CREATOR =
284             new Creator<CellIdentityNr>() {
285                 @Override
286                 public CellIdentityNr createFromParcel(Parcel in) {
287                     // Skip the type info.
288                     in.readInt();
289                     return createFromParcelBody(in);
290                 }
291 
292                 @Override
293                 public CellIdentityNr[] newArray(int size) {
294                     return new CellIdentityNr[size];
295                 }
296             };
297 
298     /** @hide */
createFromParcelBody(Parcel in)299     protected static CellIdentityNr createFromParcelBody(Parcel in) {
300         return new CellIdentityNr(in);
301     }
302 }
303