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 = -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 */ CellIdentity(@ullable String tag, int type, @Nullable String mcc, @Nullable String mnc, @Nullable String alphal, @Nullable String alphas)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 describeContents()122 public int describeContents() { 123 return 0; 124 } 125 126 /** 127 * @hide 128 * @return The type of the cell identity 129 */ getType()130 public @CellInfo.Type int getType() { 131 return mType; 132 } 133 134 /** 135 * @return MCC or null for CDMA 136 * @hide 137 */ getMccString()138 public String getMccString() { 139 return mMccStr; 140 } 141 142 /** 143 * @return MNC or null for CDMA 144 * @hide 145 */ getMncString()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 */ getChannelNumber()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 getOperatorAlphaLong()165 public CharSequence getOperatorAlphaLong() { 166 return mAlphaLong; 167 } 168 169 /** 170 * @hide 171 */ setOperatorAlphaLong(String alphaLong)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 getOperatorAlphaShort()181 public CharSequence getOperatorAlphaShort() { 182 return mAlphaShort; 183 } 184 185 /** 186 * @hide 187 */ setOperatorAlphaShort(String alphaShort)188 public void setOperatorAlphaShort(String alphaShort) { 189 mAlphaShort = alphaShort; 190 } 191 192 /** 193 * @return Global Cell ID 194 * @hide 195 */ 196 @Nullable getGlobalCellId()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 */ isSameCell(@ullable CellIdentity ci)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 */ getPlmn()214 public @Nullable String getPlmn() { 215 if (mMccStr == null || mMncStr == null) return null; 216 return mMccStr + mMncStr; 217 } 218 219 /** @hide */ updateGlobalCellId()220 protected abstract void updateGlobalCellId(); 221 222 /** 223 * @return a CellLocation object for this CellIdentity 224 * @hide 225 */ 226 @SystemApi asCellLocation()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 sanitizeLocationInfo()236 public abstract @NonNull CellIdentity sanitizeLocationInfo(); 237 238 @Override equals(Object other)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 hashCode()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 writeToParcel(Parcel dest, int type)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 */ isValidPlmn(@onNull String plmn)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 */ CellIdentity(String tag, int type, Parcel source)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 */ log(String s)316 protected void log(String s) { 317 Rlog.w(mTag, s); 318 } 319 320 /** @hide */ inRangeOrUnavailable(int value, int rangeMin, int rangeMax)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 */ inRangeOrUnavailable(long value, long rangeMin, long rangeMax)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 */ inRangeOrUnavailable( int value, int rangeMin, int rangeMax, int special)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 */ isMcc(@onNull String mcc)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 */ isMnc(@onNull String mnc)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 */ create(android.hardware.radio.V1_0.CellIdentity cellIdentity)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 */ create(android.hardware.radio.V1_2.CellIdentity cellIdentity)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 */ create(android.hardware.radio.V1_5.CellIdentity ci)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 } 465