1 /*
2  * Copyright (C) 2012 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.os.Build;
23 import android.os.Parcel;
24 import android.telephony.gsm.GsmCellLocation;
25 import android.text.TextUtils;
26 import android.util.ArraySet;
27 
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Objects;
32 import java.util.Set;
33 
34 /**
35  * CellIdentity is to represent a unique LTE cell
36  */
37 public final class CellIdentityLte extends CellIdentity {
38     private static final String TAG = CellIdentityLte.class.getSimpleName();
39     private static final boolean DBG = false;
40 
41     private static final int MAX_CI = 268435455;
42     private static final int MAX_PCI = 503;
43     private static final int MAX_TAC = 65535;
44     private static final int MAX_EARFCN = 262143;
45     private static final int MAX_BANDWIDTH = 20000;
46 
47     // 28-bit cell identity
48     private final int mCi;
49     // physical cell id 0..503
50     private final int mPci;
51     // 16-bit tracking area code
52     private final int mTac;
53     // 18-bit Absolute RF Channel Number
54     private final int mEarfcn;
55     // cell bandwidth, in kHz
56     private final int mBandwidth;
57     // cell bands
58     private final int[] mBands;
59 
60     // a list of additional PLMN-IDs reported for this cell
61     private final ArraySet<String> mAdditionalPlmns;
62 
63     private ClosedSubscriberGroupInfo mCsgInfo;
64 
65     /**
66      * @hide
67      */
68     @UnsupportedAppUsage
CellIdentityLte()69     public CellIdentityLte() {
70         super(TAG, CellInfo.TYPE_LTE, null, null, null, null);
71         mCi = CellInfo.UNAVAILABLE;
72         mPci = CellInfo.UNAVAILABLE;
73         mTac = CellInfo.UNAVAILABLE;
74         mEarfcn = CellInfo.UNAVAILABLE;
75         mBands = new int[] {};
76         mBandwidth = CellInfo.UNAVAILABLE;
77         mAdditionalPlmns = new ArraySet<>();
78         mCsgInfo = null;
79         mGlobalCellId = null;
80     }
81 
82     /**
83      *
84      * @param mcc 3-digit Mobile Country Code, 0..999
85      * @param mnc 2 or 3-digit Mobile Network Code, 0..999
86      * @param ci 28-bit Cell Identity
87      * @param pci Physical Cell Id 0..503
88      * @param tac 16-bit Tracking Area Code
89      *
90      * @hide
91      */
92     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac)93     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
94         this(ci, pci, tac, CellInfo.UNAVAILABLE, new int[] {}, CellInfo.UNAVAILABLE,
95                 String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(),
96                 null);
97     }
98 
99     /**
100      *
101      * @param ci 28-bit Cell Identity
102      * @param pci Physical Cell Id 0..503
103      * @param tac 16-bit Tracking Area Code
104      * @param earfcn 18-bit LTE Absolute RF Channel Number
105      * @param bandwidth cell bandwidth in kHz
106      * @param mccStr 3-digit Mobile Country Code in string format
107      * @param mncStr 2 or 3-digit Mobile Network Code in string format
108      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
109      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
110      * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
111      * @param csgInfo info about the closed subscriber group broadcast by the cell
112      *
113      * @hide
114      */
CellIdentityLte(int ci, int pci, int tac, int earfcn, @NonNull int[] bands, int bandwidth, @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas, @NonNull Collection<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo)115     public CellIdentityLte(int ci, int pci, int tac, int earfcn, @NonNull int[] bands,
116             int bandwidth, @Nullable String mccStr, @Nullable String mncStr,
117             @Nullable String alphal, @Nullable String alphas,
118             @NonNull Collection<String> additionalPlmns,
119             @Nullable ClosedSubscriberGroupInfo csgInfo) {
120         super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
121         mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
122         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
123         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
124         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
125         mBands = bands;
126         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
127         mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
128         for (String plmn : additionalPlmns) {
129             if (isValidPlmn(plmn)) {
130                 mAdditionalPlmns.add(plmn);
131             }
132         }
133         mCsgInfo = csgInfo;
134         updateGlobalCellId();
135     }
136 
137     /** @hide */
CellIdentityLte(@onNull android.hardware.radio.V1_0.CellIdentityLte cid)138     public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
139         this(cid.ci, cid.pci, cid.tac, cid.earfcn, new int[] {},
140                 CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
141     }
142 
143     /** @hide */
CellIdentityLte(@onNull android.hardware.radio.V1_2.CellIdentityLte cid)144     public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
145         this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, new int[] {},
146                 cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
147                 cid.operatorNames.alphaShort, new ArraySet<>(), null);
148     }
149 
150     /** @hide */
CellIdentityLte(@onNull android.hardware.radio.V1_5.CellIdentityLte cid)151     public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) {
152         this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
153                 cid.bands.stream().mapToInt(Integer::intValue).toArray(), cid.base.bandwidth,
154                 cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
155                 cid.base.operatorNames.alphaShort, cid.additionalPlmns,
156                 cid.optionalCsgInfo.getDiscriminator()
157                         == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
158                                 ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
159                                         : null);
160     }
161 
CellIdentityLte(@onNull CellIdentityLte cid)162     private CellIdentityLte(@NonNull CellIdentityLte cid) {
163         this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr,
164                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
165     }
166 
167     /** @hide */
168     @Override
sanitizeLocationInfo()169     public @NonNull CellIdentityLte sanitizeLocationInfo() {
170         return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
171                 CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE,
172                 mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
173     }
174 
copy()175     @NonNull CellIdentityLte copy() {
176         return new CellIdentityLte(this);
177     }
178 
179     /** @hide */
180     @Override
updateGlobalCellId()181     protected void updateGlobalCellId() {
182         mGlobalCellId = null;
183         String plmn = getPlmn();
184         if (plmn == null) return;
185 
186         if (mCi == CellInfo.UNAVAILABLE) return;
187 
188         mGlobalCellId = plmn + String.format("%07x", mCi);
189     }
190 
191     /**
192      * @return 3-digit Mobile Country Code, 0..999,
193      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
194      * @deprecated Use {@link #getMccString} instead.
195      */
196     @Deprecated
getMcc()197     public int getMcc() {
198         return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
199     }
200 
201     /**
202      * @return 2 or 3-digit Mobile Network Code, 0..999,
203      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
204      * @deprecated Use {@link #getMncString} instead.
205      */
206     @Deprecated
getMnc()207     public int getMnc() {
208         return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
209     }
210 
211     /**
212      * @return 28-bit Cell Identity,
213      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
214      */
getCi()215     public int getCi() {
216         return mCi;
217     }
218 
219     /**
220      * @return Physical Cell Id 0..503,
221      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
222      */
getPci()223     public int getPci() {
224         return mPci;
225     }
226 
227     /**
228      * @return 16-bit Tracking Area Code,
229      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
230      */
getTac()231     public int getTac() {
232         return mTac;
233     }
234 
235     /**
236      * @return 18-bit Absolute RF Channel Number,
237      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
238      */
getEarfcn()239     public int getEarfcn() {
240         return mEarfcn;
241     }
242 
243     /**
244      * Get bands of the cell
245      *
246      * Reference: 3GPP TS 36.101 section 5.5
247      *
248      * @return Array of band number or empty array if not available.
249      */
250     @NonNull
getBands()251     public int[] getBands() {
252         return Arrays.copyOf(mBands, mBands.length);
253     }
254 
255     /**
256      * @return Cell bandwidth in kHz,
257      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
258      */
getBandwidth()259     public int getBandwidth() {
260         return mBandwidth;
261     }
262 
263     /**
264      * @return Mobile Country Code in string format, null if unavailable.
265      */
266     @Nullable
getMccString()267     public String getMccString() {
268         return mMccStr;
269     }
270 
271     /**
272      * @return Mobile Network Code in string format, null if unavailable.
273      */
274     @Nullable
getMncString()275     public String getMncString() {
276         return mMncStr;
277     }
278 
279     /**
280      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
281      */
282     @Nullable
getMobileNetworkOperator()283     public String getMobileNetworkOperator() {
284         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
285     }
286 
287     /** @hide */
288     @Override
getChannelNumber()289     public int getChannelNumber() {
290         return mEarfcn;
291     }
292 
293     /**
294      * @return a list of additional PLMN IDs supported by this cell.
295      */
296     @NonNull
getAdditionalPlmns()297     public Set<String> getAdditionalPlmns() {
298         return Collections.unmodifiableSet(mAdditionalPlmns);
299     }
300 
301     /**
302      * @return closed subscriber group information about the cell if available, otherwise null.
303      */
304     @Nullable
getClosedSubscriberGroupInfo()305     public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
306         return mCsgInfo;
307     }
308 
309     /**
310      * A hack to allow tunneling of LTE information via GsmCellLocation
311      * so that older Network Location Providers can return some information
312      * on LTE only networks, see bug 9228974.
313      *
314      * The tunnel'd LTE information is returned as follows:
315      *   LAC = TAC field
316      *   CID = CI field
317      *   PSC = 0.
318      *
319      * @hide
320      */
321     @NonNull
322     @Override
asCellLocation()323     public GsmCellLocation asCellLocation() {
324         GsmCellLocation cl = new GsmCellLocation();
325         int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
326         int cid = mCi != CellInfo.UNAVAILABLE ? mCi : -1;
327         cl.setLacAndCid(tac, cid);
328         cl.setPsc(0);
329         return cl;
330     }
331 
332     @Override
hashCode()333     public int hashCode() {
334         return Objects.hash(mCi, mPci, mTac, mEarfcn, Arrays.hashCode(mBands),
335                 mBandwidth, mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
336     }
337 
338     @Override
equals(Object other)339     public boolean equals(Object other) {
340         if (this == other) {
341             return true;
342         }
343 
344         if (!(other instanceof CellIdentityLte)) {
345             return false;
346         }
347 
348         CellIdentityLte o = (CellIdentityLte) other;
349         return mCi == o.mCi
350                 && mPci == o.mPci
351                 && mTac == o.mTac
352                 && mEarfcn == o.mEarfcn
353                 && Arrays.equals(mBands, o.mBands)
354                 && mBandwidth == o.mBandwidth
355                 && TextUtils.equals(mMccStr, o.mMccStr)
356                 && TextUtils.equals(mMncStr, o.mMncStr)
357                 && mAdditionalPlmns.equals(o.mAdditionalPlmns)
358                 && Objects.equals(mCsgInfo, o.mCsgInfo)
359                 && super.equals(other);
360     }
361 
362     @Override
toString()363     public String toString() {
364         return new StringBuilder(TAG)
365         .append(":{ mCi=").append(mCi)
366         .append(" mPci=").append(mPci)
367         .append(" mTac=").append(mTac)
368         .append(" mEarfcn=").append(mEarfcn)
369         .append(" mBands=").append(mBands)
370         .append(" mBandwidth=").append(mBandwidth)
371         .append(" mMcc=").append(mMccStr)
372         .append(" mMnc=").append(mMncStr)
373         .append(" mAlphaLong=").append(mAlphaLong)
374         .append(" mAlphaShort=").append(mAlphaShort)
375         .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
376         .append(" mCsgInfo=").append(mCsgInfo)
377         .append("}").toString();
378     }
379 
380     /** Implement the Parcelable interface */
381     @Override
writeToParcel(Parcel dest, int flags)382     public void writeToParcel(Parcel dest, int flags) {
383         if (DBG) log("writeToParcel(Parcel, int): " + toString());
384         super.writeToParcel(dest, CellInfo.TYPE_LTE);
385         dest.writeInt(mCi);
386         dest.writeInt(mPci);
387         dest.writeInt(mTac);
388         dest.writeInt(mEarfcn);
389         dest.writeIntArray(mBands);
390         dest.writeInt(mBandwidth);
391         dest.writeArraySet(mAdditionalPlmns);
392         dest.writeParcelable(mCsgInfo, flags);
393     }
394 
395     /** Construct from Parcel, type has already been processed */
CellIdentityLte(Parcel in)396     private CellIdentityLte(Parcel in) {
397         super(TAG, CellInfo.TYPE_LTE, in);
398         mCi = in.readInt();
399         mPci = in.readInt();
400         mTac = in.readInt();
401         mEarfcn = in.readInt();
402         mBands = in.createIntArray();
403         mBandwidth = in.readInt();
404         mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
405         mCsgInfo = in.readParcelable(null);
406 
407         updateGlobalCellId();
408         if (DBG) log(toString());
409     }
410 
411     /** Implement the Parcelable interface */
412     @SuppressWarnings("hiding")
413     public static final @android.annotation.NonNull Creator<CellIdentityLte> CREATOR =
414             new Creator<CellIdentityLte>() {
415                 @Override
416                 public CellIdentityLte createFromParcel(Parcel in) {
417                     in.readInt();   // skip;
418                     return createFromParcelBody(in);
419                 }
420 
421                 @Override
422                 public CellIdentityLte[] newArray(int size) {
423                     return new CellIdentityLte[size];
424                 }
425             };
426 
427     /** @hide */
createFromParcelBody(Parcel in)428     protected static CellIdentityLte createFromParcelBody(Parcel in) {
429         return new CellIdentityLte(in);
430     }
431 }
432