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