• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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.CallSuper;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.hardware.radio.V1_0.CellInfoType;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.text.TextUtils;
27 
28 import com.android.telephony.Rlog;
29 
30 import java.util.Objects;
31 import java.util.UUID;
32 
33 /**
34  * CellIdentity represents the identity of a unique cell. This is the base class for
35  * CellIdentityXxx which represents cell identity for specific network access technology.
36  */
37 public abstract class CellIdentity implements Parcelable {
38 
39     /** @hide */
40     public static final int INVALID_CHANNEL_NUMBER = Integer.MAX_VALUE;
41 
42     /**
43      * parameters for validation
44      * @hide
45      */
46     public static final int MCC_LENGTH = 3;
47 
48     /** @hide */
49     public static final int MNC_MIN_LENGTH = 2;
50     /** @hide */
51     public static final int MNC_MAX_LENGTH = 3;
52 
53     // Log tag
54     /** @hide */
55     protected final String mTag;
56     // Cell identity type
57     /** @hide */
58     protected final int mType;
59     // 3-digit Mobile Country Code in string format. Null for CDMA cell identity.
60     /** @hide */
61     protected final String mMccStr;
62     // 2 or 3-digit Mobile Network Code in string format. Null for CDMA cell identity.
63     /** @hide */
64     protected final String mMncStr;
65 
66     // long alpha Operator Name String or Enhanced Operator Name String
67     /** @hide */
68     protected String mAlphaLong;
69     // short alpha Operator Name String or Enhanced Operator Name String
70     /** @hide */
71     protected String mAlphaShort;
72 
73     // Cell Global, 3GPP TS 23.003
74     /** @hide */
75     protected String mGlobalCellId;
76 
77 
78     /** @hide */
CellIdentity(@ullable String tag, int type, @Nullable String mcc, @Nullable String mnc, @Nullable String alphal, @Nullable String alphas)79     protected CellIdentity(@Nullable String tag, int type, @Nullable String mcc,
80             @Nullable String mnc, @Nullable String alphal, @Nullable String alphas) {
81         mTag = tag;
82         mType = type;
83 
84         // Only allow INT_MAX if unknown string mcc/mnc
85         if (mcc == null || isMcc(mcc)) {
86             mMccStr = mcc;
87         } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
88             // If the mccStr is empty or unknown, set it as null.
89             mMccStr = null;
90         } else {
91             // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
92             // after the bug got fixed.
93             mMccStr = null;
94             log("invalid MCC format: " + mcc);
95         }
96 
97         if (mnc == null || isMnc(mnc)) {
98             mMncStr = mnc;
99         } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
100             // If the mncStr is empty or unknown, set it as null.
101             mMncStr = null;
102         } else {
103             // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
104             // after the bug got fixed.
105             mMncStr = null;
106             log("invalid MNC format: " + mnc);
107         }
108 
109         if ((mMccStr != null && mMncStr == null) || (mMccStr == null && mMncStr != null)) {
110             AnomalyReporter.reportAnomaly(
111                     UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
112                     "CellIdentity Missing Half of PLMN ID");
113         }
114 
115         mAlphaLong = alphal;
116         mAlphaShort = alphas;
117     }
118 
119     /** Implement the Parcelable interface */
120     @Override
describeContents()121     public int describeContents() {
122         return 0;
123     }
124 
125     /**
126      * @hide
127      * @return The type of the cell identity
128      */
getType()129     public @CellInfo.Type int getType() {
130         return mType;
131     }
132 
133     /**
134      * @return MCC or null for CDMA
135      * @hide
136      */
getMccString()137     public String getMccString() {
138         return mMccStr;
139     }
140 
141     /**
142      * @return MNC or null for CDMA
143      * @hide
144      */
getMncString()145     public String getMncString() {
146         return mMncStr;
147     }
148 
149     /**
150      * Returns the channel number of the cell identity.
151      *
152      * @hide
153      * @return The channel number, or {@link #INVALID_CHANNEL_NUMBER} if not implemented
154      */
getChannelNumber()155     public int getChannelNumber() {
156         return INVALID_CHANNEL_NUMBER;
157     }
158 
159     /**
160      * @return The long alpha tag associated with the current scan result (may be the operator
161      * name string or extended operator name string). May be null if unknown.
162      */
163     @Nullable
getOperatorAlphaLong()164     public CharSequence getOperatorAlphaLong() {
165         return mAlphaLong;
166     }
167 
168     /**
169      * @hide
170      */
setOperatorAlphaLong(String alphaLong)171     public void setOperatorAlphaLong(String alphaLong) {
172         mAlphaLong = alphaLong;
173     }
174 
175     /**
176      * @return The short alpha tag associated with the current scan result (may be the operator
177      * name string or extended operator name string).  May be null if unknown.
178      */
179     @Nullable
getOperatorAlphaShort()180     public CharSequence getOperatorAlphaShort() {
181         return mAlphaShort;
182     }
183 
184     /**
185      * @hide
186      */
setOperatorAlphaShort(String alphaShort)187     public void setOperatorAlphaShort(String alphaShort) {
188         mAlphaShort = alphaShort;
189     }
190 
191     /**
192      * @return Global Cell ID
193      * @hide
194      */
195     @Nullable
getGlobalCellId()196     public String getGlobalCellId() {
197         return mGlobalCellId;
198     }
199 
200     /**
201      * @param ci a CellIdentity to compare to the current CellIdentity.
202      * @return true if ci has the same technology and Global Cell ID; false, otherwise.
203      * @hide
204      */
isSameCell(@ullable CellIdentity ci)205     public boolean isSameCell(@Nullable CellIdentity ci) {
206         if (ci == null) return false;
207         if (this.getClass() != ci.getClass()) return false;
208         return TextUtils.equals(this.getGlobalCellId(), ci.getGlobalCellId());
209     }
210 
211     /** @hide */
getPlmn()212     public @Nullable String getPlmn() {
213         if (mMccStr == null || mMncStr == null) return null;
214         return mMccStr + mMncStr;
215     }
216 
217     /** @hide */
updateGlobalCellId()218     protected abstract void updateGlobalCellId();
219 
220     /**
221      * @return a CellLocation object for this CellIdentity
222      * @hide
223      */
224     @SystemApi
asCellLocation()225     public abstract @NonNull CellLocation asCellLocation();
226 
227     /**
228      * Create and a return a new instance of CellIdentity with location-identifying information
229      * removed.
230      *
231      * @hide
232      */
233     @SystemApi
sanitizeLocationInfo()234     public abstract @NonNull CellIdentity sanitizeLocationInfo();
235 
236     @Override
equals(Object other)237     public boolean equals(Object other) {
238         if (!(other instanceof CellIdentity)) {
239             return false;
240         }
241 
242         CellIdentity o = (CellIdentity) other;
243         return mType == o.mType
244                 && TextUtils.equals(mMccStr, o.mMccStr)
245                 && TextUtils.equals(mMncStr, o.mMncStr)
246                 && TextUtils.equals(mAlphaLong, o.mAlphaLong)
247                 && TextUtils.equals(mAlphaShort, o.mAlphaShort);
248     }
249 
250     @Override
hashCode()251     public int hashCode() {
252         return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
253     }
254 
255     /**
256      * Used by child classes for parceling.
257      *
258      * @hide
259      */
260     @CallSuper
writeToParcel(Parcel dest, int type)261     public void writeToParcel(Parcel dest, int type) {
262         dest.writeInt(type);
263         dest.writeString(mMccStr);
264         dest.writeString(mMncStr);
265         dest.writeString(mAlphaLong);
266         dest.writeString(mAlphaShort);
267     }
268 
269     /** Used by phone interface manager to verify if a given string is valid MccMnc
270      * @hide
271      */
isValidPlmn(@onNull String plmn)272     public static boolean isValidPlmn(@NonNull String plmn) {
273         if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH
274                 || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) {
275             return false;
276         }
277         return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH)));
278     }
279 
280     /**
281      * Construct from Parcel
282      * @hide
283      */
CellIdentity(String tag, int type, Parcel source)284     protected CellIdentity(String tag, int type, Parcel source) {
285         this(tag, type, source.readString(), source.readString(),
286                 source.readString(), source.readString());
287     }
288 
289     /** Implement the Parcelable interface */
290     public static final @android.annotation.NonNull Creator<CellIdentity> CREATOR =
291             new Creator<CellIdentity>() {
292                 @Override
293                 public CellIdentity createFromParcel(Parcel in) {
294                     int type = in.readInt();
295                     switch (type) {
296                         case CellInfo.TYPE_GSM: return CellIdentityGsm.createFromParcelBody(in);
297                         case CellInfo.TYPE_WCDMA: return CellIdentityWcdma.createFromParcelBody(in);
298                         case CellInfo.TYPE_CDMA: return CellIdentityCdma.createFromParcelBody(in);
299                         case CellInfo.TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
300                         case CellInfo.TYPE_TDSCDMA:
301                             return CellIdentityTdscdma.createFromParcelBody(in);
302                         case CellInfo.TYPE_NR: return CellIdentityNr.createFromParcelBody(in);
303                         default: throw new IllegalArgumentException("Bad Cell identity Parcel");
304                     }
305                 }
306 
307                 @Override
308                 public CellIdentity[] newArray(int size) {
309                     return new CellIdentity[size];
310                 }
311             };
312 
313     /** @hide */
log(String s)314     protected void log(String s) {
315         Rlog.w(mTag, s);
316     }
317 
318     /** @hide */
inRangeOrUnavailable(int value, int rangeMin, int rangeMax)319     protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
320         if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
321         return value;
322     }
323 
324     /** @hide */
inRangeOrUnavailable(long value, long rangeMin, long rangeMax)325     protected static final long inRangeOrUnavailable(long value, long rangeMin, long rangeMax) {
326         if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE_LONG;
327         return value;
328     }
329 
330     /** @hide */
inRangeOrUnavailable( int value, int rangeMin, int rangeMax, int special)331     protected static final int inRangeOrUnavailable(
332             int value, int rangeMin, int rangeMax, int special) {
333         if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
334         return value;
335     }
336 
337     /** @hide */
isMcc(@onNull String mcc)338     private static boolean isMcc(@NonNull String mcc) {
339         // ensure no out of bounds indexing
340         if (mcc.length() != MCC_LENGTH) return false;
341 
342         // Character.isDigit allows all unicode digits, not just [0-9]
343         for (int i = 0; i < MCC_LENGTH; i++) {
344             if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
345         }
346 
347         return true;
348     }
349 
350     /** @hide */
isMnc(@onNull String mnc)351     private static boolean isMnc(@NonNull String mnc) {
352         // ensure no out of bounds indexing
353         if (mnc.length() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) return false;
354 
355         // Character.isDigit allows all unicode digits, not just [0-9]
356         for (int i = 0; i < mnc.length(); i++) {
357             if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
358         }
359 
360         return true;
361     }
362 
363     /** @hide */
create(android.hardware.radio.V1_0.CellIdentity cellIdentity)364     public static CellIdentity create(android.hardware.radio.V1_0.CellIdentity cellIdentity) {
365         if (cellIdentity == null)  return null;
366         switch(cellIdentity.cellInfoType) {
367             case CellInfoType.GSM: {
368                 if (cellIdentity.cellIdentityGsm.size() == 1) {
369                     return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0));
370                 }
371                 break;
372             }
373             case CellInfoType.WCDMA: {
374                 if (cellIdentity.cellIdentityWcdma.size() == 1) {
375                     return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0));
376                 }
377                 break;
378             }
379             case CellInfoType.TD_SCDMA: {
380                 if (cellIdentity.cellIdentityTdscdma.size() == 1) {
381                     return new  CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0));
382                 }
383                 break;
384             }
385             case CellInfoType.LTE: {
386                 if (cellIdentity.cellIdentityLte.size() == 1) {
387                     return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0));
388                 }
389                 break;
390             }
391             case CellInfoType.CDMA: {
392                 if (cellIdentity.cellIdentityCdma.size() == 1) {
393                     return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0));
394                 }
395                 break;
396             }
397             case CellInfoType.NONE: break;
398             default: break;
399         }
400         return null;
401     }
402 
403     /** @hide */
create(android.hardware.radio.V1_2.CellIdentity cellIdentity)404     public static CellIdentity create(android.hardware.radio.V1_2.CellIdentity cellIdentity) {
405         if (cellIdentity == null)  return null;
406         switch(cellIdentity.cellInfoType) {
407             case CellInfoType.GSM: {
408                 if (cellIdentity.cellIdentityGsm.size() == 1) {
409                     return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0));
410                 }
411                 break;
412             }
413             case CellInfoType.WCDMA: {
414                 if (cellIdentity.cellIdentityWcdma.size() == 1) {
415                     return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0));
416                 }
417                 break;
418             }
419             case CellInfoType.TD_SCDMA: {
420                 if (cellIdentity.cellIdentityTdscdma.size() == 1) {
421                     return new  CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0));
422                 }
423                 break;
424             }
425             case CellInfoType.LTE: {
426                 if (cellIdentity.cellIdentityLte.size() == 1) {
427                     return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0));
428                 }
429                 break;
430             }
431             case CellInfoType.CDMA: {
432                 if (cellIdentity.cellIdentityCdma.size() == 1) {
433                     return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0));
434                 }
435                 break;
436             }
437             case CellInfoType.NONE: break;
438             default: break;
439         }
440         return null;
441     }
442 
443     /** @hide */
create(android.hardware.radio.V1_5.CellIdentity ci)444     public static CellIdentity create(android.hardware.radio.V1_5.CellIdentity ci) {
445         if (ci == null) return null;
446         switch (ci.getDiscriminator()) {
447             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.gsm:
448                 return new CellIdentityGsm(ci.gsm());
449             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.cdma:
450                 return new CellIdentityCdma(ci.cdma());
451             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.lte:
452                 return new CellIdentityLte(ci.lte());
453             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.wcdma:
454                 return new CellIdentityWcdma(ci.wcdma());
455             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.tdscdma:
456                 return new CellIdentityTdscdma(ci.tdscdma());
457             case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.nr:
458                 return new CellIdentityNr(ci.nr());
459             default: return null;
460         }
461     }
462 }
463