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