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