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