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.Collection;
31 import java.util.Collections;
32 import java.util.Objects;
33 import java.util.Set;
34 
35 /**
36  * CellIdentity to represent a unique GSM cell
37  */
38 public final class CellIdentityGsm extends CellIdentity {
39     private static final String TAG = CellIdentityGsm.class.getSimpleName();
40     private static final boolean DBG = false;
41 
42     private static final int MAX_LAC = 65535;
43     private static final int MAX_CID = 65535;
44     private static final int MAX_ARFCN = 65535;
45     private static final int MAX_BSIC = 63;
46 
47     // 16-bit Location Area Code, 0..65535
48     private final int mLac;
49     // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
50     private final int mCid;
51     // 16-bit GSM Absolute RF Channel Number
52     private final int mArfcn;
53     // 6-bit Base Station Identity Code
54     private final int mBsic;
55 
56     // a list of additional PLMN-IDs reported for this cell
57     private final ArraySet<String> mAdditionalPlmns;
58 
59     /**
60      * @hide
61      */
62     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
CellIdentityGsm()63     public CellIdentityGsm() {
64         super(TAG, CellInfo.TYPE_GSM, null, null, null, null);
65         mLac = CellInfo.UNAVAILABLE;
66         mCid = CellInfo.UNAVAILABLE;
67         mArfcn = CellInfo.UNAVAILABLE;
68         mBsic = CellInfo.UNAVAILABLE;
69         mAdditionalPlmns = new ArraySet<>();
70         mGlobalCellId = null;
71     }
72 
73     /**
74      * public constructor
75      * @param lac 16-bit Location Area Code, 0..65535
76      * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
77      * @param arfcn 16-bit GSM Absolute RF Channel Number
78      * @param bsic 6-bit Base Station Identity Code
79      * @param mccStr 3-digit Mobile Country Code in string format
80      * @param mncStr 2 or 3-digit Mobile Network Code in string format
81      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
82      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
83      * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
84      *
85      * @hide
86      */
CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas, @NonNull Collection<String> additionalPlmns)87     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
88             @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
89             @NonNull Collection<String> additionalPlmns) {
90         super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
91         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
92         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
93         mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
94         mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
95         mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
96         for (String plmn : additionalPlmns) {
97             if (isValidPlmn(plmn)) {
98                 mAdditionalPlmns.add(plmn);
99             }
100         }
101         updateGlobalCellId();
102     }
103 
CellIdentityGsm(@onNull CellIdentityGsm cid)104     private CellIdentityGsm(@NonNull CellIdentityGsm cid) {
105         this(cid.mLac, cid.mCid, cid.mArfcn, cid.mBsic, cid.mMccStr,
106                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns);
107     }
108 
copy()109     @NonNull CellIdentityGsm copy() {
110         return new CellIdentityGsm(this);
111     }
112 
113     /** @hide */
114     @Override
sanitizeLocationInfo()115     public @NonNull CellIdentityGsm sanitizeLocationInfo() {
116         return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
117                 CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns);
118     }
119 
120     /** @hide */
121     @Override
updateGlobalCellId()122     protected void updateGlobalCellId() {
123         mGlobalCellId = null;
124         String plmn = getPlmn();
125         if (plmn == null) return;
126 
127         if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
128 
129         mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
130     }
131 
132     /**
133      * @return 3-digit Mobile Country Code, 0..999,
134      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
135      * @deprecated Use {@link #getMccString} instead.
136      */
137     @Deprecated
getMcc()138     public int getMcc() {
139         return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
140     }
141 
142     /**
143      * @return 2 or 3-digit Mobile Network Code, 0..999,
144      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
145      * @deprecated Use {@link #getMncString} instead.
146      */
147     @Deprecated
getMnc()148     public int getMnc() {
149         return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
150     }
151 
152     /**
153      * @return 16-bit Location Area Code, 0..65535,
154      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
155      */
getLac()156     public int getLac() {
157         return mLac;
158     }
159 
160     /**
161      * @return 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
162      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
163      */
getCid()164     public int getCid() {
165         return mCid;
166     }
167 
168     /**
169      * @return 16-bit GSM Absolute RF Channel Number,
170      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
171      */
getArfcn()172     public int getArfcn() {
173         return mArfcn;
174     }
175 
176     /**
177      * @return 6-bit Base Station Identity Code,
178      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
179      */
getBsic()180     public int getBsic() {
181         return mBsic;
182     }
183 
184     /**
185      * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
186      */
187     @Nullable
getMobileNetworkOperator()188     public String getMobileNetworkOperator() {
189         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
190     }
191 
192     /**
193      * @return Mobile Country Code in string format, null if unavailable.
194      */
195     @Nullable
getMccString()196     public String getMccString() {
197         return mMccStr;
198     }
199 
200     /**
201      * @return Mobile Network Code in string format, null if unavailable.
202      */
203     @Nullable
getMncString()204     public String getMncString() {
205         return mMncStr;
206     }
207 
208     /** @hide */
209     @Override
getChannelNumber()210     public int getChannelNumber() {
211         return mArfcn;
212     }
213 
214     /**
215      * @return a list of additional PLMN IDs supported by this cell.
216      */
217     @NonNull
getAdditionalPlmns()218     public Set<String> getAdditionalPlmns() {
219         return Collections.unmodifiableSet(mAdditionalPlmns);
220     }
221 
222     /**
223      * @deprecated Primary Scrambling Code is not applicable to GSM.
224      * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
225      */
226     @Deprecated
getPsc()227     public int getPsc() {
228         return CellInfo.UNAVAILABLE;
229     }
230 
231     /** @hide */
232     @NonNull
233     @Override
asCellLocation()234     public GsmCellLocation asCellLocation() {
235         GsmCellLocation cl = new GsmCellLocation();
236         int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
237         int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
238         cl.setLacAndCid(lac, cid);
239         cl.setPsc(-1);
240         return cl;
241     }
242 
243     @Override
hashCode()244     public int hashCode() {
245         return Objects.hash(mLac, mCid, mAdditionalPlmns.hashCode(), super.hashCode());
246     }
247 
248     @Override
equals(Object other)249     public boolean equals(Object other) {
250         if (this == other) {
251             return true;
252         }
253 
254         if (!(other instanceof CellIdentityGsm)) {
255             return false;
256         }
257 
258         CellIdentityGsm o = (CellIdentityGsm) other;
259         return mLac == o.mLac
260                 && mCid == o.mCid
261                 && mArfcn == o.mArfcn
262                 && mBsic == o.mBsic
263                 && TextUtils.equals(mMccStr, o.mMccStr)
264                 && TextUtils.equals(mMncStr, o.mMncStr)
265                 && mAdditionalPlmns.equals(o.mAdditionalPlmns)
266                 && super.equals(other);
267     }
268 
269     @Override
toString()270     public String toString() {
271         return new StringBuilder(TAG)
272         .append(":{ mLac=").append(mLac)
273         .append(" mCid=").append(mCid)
274         .append(" mArfcn=").append(mArfcn)
275         .append(" mBsic=").append("0x").append(Integer.toHexString(mBsic))
276         .append(" mMcc=").append(mMccStr)
277         .append(" mMnc=").append(mMncStr)
278         .append(" mAlphaLong=").append(mAlphaLong)
279         .append(" mAlphaShort=").append(mAlphaShort)
280         .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
281         .append("}").toString();
282     }
283 
284     /** Implement the Parcelable interface */
285     @Override
writeToParcel(Parcel dest, int flags)286     public void writeToParcel(Parcel dest, int flags) {
287         if (DBG) log("writeToParcel(Parcel, int): " + toString());
288         super.writeToParcel(dest, CellInfo.TYPE_GSM);
289         dest.writeInt(mLac);
290         dest.writeInt(mCid);
291         dest.writeInt(mArfcn);
292         dest.writeInt(mBsic);
293         dest.writeArraySet(mAdditionalPlmns);
294     }
295 
296     /** Construct from Parcel, type has already been processed */
CellIdentityGsm(Parcel in)297     private CellIdentityGsm(Parcel in) {
298         super(TAG, CellInfo.TYPE_GSM, in);
299         mLac = in.readInt();
300         mCid = in.readInt();
301         mArfcn = in.readInt();
302         mBsic = in.readInt();
303         mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
304 
305         updateGlobalCellId();
306         if (DBG) log(toString());
307     }
308 
309     /** Implement the Parcelable interface */
310     @SuppressWarnings("hiding")
311     public static final @android.annotation.NonNull Creator<CellIdentityGsm> CREATOR =
312             new Creator<CellIdentityGsm>() {
313                 @Override
314                 public CellIdentityGsm createFromParcel(Parcel in) {
315                     in.readInt();   // skip
316                     return createFromParcelBody(in);
317                 }
318 
319                 @Override
320                 public CellIdentityGsm[] newArray(int size) {
321                     return new CellIdentityGsm[size];
322                 }
323             };
324 
325     /** @hide */
createFromParcelBody(Parcel in)326     protected static CellIdentityGsm createFromParcelBody(Parcel in) {
327         return new CellIdentityGsm(in);
328     }
329 }
330