1 /* 2 * Copyright (C) 2016 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 com.android.server.wifi; 18 19 import android.text.TextUtils; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 23 /** 24 * Class for storing an IMSI (International Mobile Subscriber Identity) parameter. The IMSI 25 * contains number (up to 15) of numerical digits. When an IMSI ends with a '*', the specified 26 * IMSI is a prefix. 27 */ 28 public class IMSIParameter { 29 /** 30 * Per 2.2 of 3GPP TS 23.003 31 * MCC (Mobile Country Code) is a 3 digit number and MNC (Mobile Network Code) is a 2 32 * or 3 digit number; 33 * The max length of IMSI is 15; 34 */ 35 public static final int MCC_MNC_LENGTH_5 = 5; 36 public static final int MCC_MNC_LENGTH_6 = 6; 37 private static final int MAX_IMSI_LENGTH = 15; 38 39 private final String mImsi; 40 private final boolean mPrefix; 41 42 @VisibleForTesting IMSIParameter(String imsi, boolean prefix)43 public IMSIParameter(String imsi, boolean prefix) { 44 mImsi = imsi; 45 mPrefix = prefix; 46 } 47 48 /** 49 * Build an IMSIParameter object from the given string. A null will be returned for a 50 * malformed string. 51 * 52 * @param imsi The IMSI string 53 * @return {@link IMSIParameter} 54 */ build(String imsi)55 public static IMSIParameter build(String imsi) { 56 if (TextUtils.isEmpty(imsi)) { 57 return null; 58 } 59 if (imsi.length() > MAX_IMSI_LENGTH) { 60 return null; 61 } 62 63 // Detect the first non-digit character. 64 int nonDigitIndex; 65 char stopChar = '\0'; 66 for (nonDigitIndex = 0; nonDigitIndex < imsi.length(); nonDigitIndex++) { 67 stopChar = imsi.charAt(nonDigitIndex); 68 if (stopChar < '0' || stopChar > '9') { 69 break; 70 } 71 } 72 73 if (nonDigitIndex == imsi.length()) { 74 // Full IMSI. 75 return new IMSIParameter(imsi, false); 76 } else if (nonDigitIndex == imsi.length() - 1 && stopChar == '*' 77 && (nonDigitIndex == MCC_MNC_LENGTH_5 || nonDigitIndex == MCC_MNC_LENGTH_6)) { 78 // IMSI prefix. 79 return new IMSIParameter(imsi.substring(0, nonDigitIndex), true); 80 } 81 return null; 82 } 83 84 /** 85 * Perform matching against the given full IMSI. 86 * 87 * @param fullIMSI The full IMSI to match against 88 * @return true if matched 89 */ matchesImsi(String fullIMSI)90 public boolean matchesImsi(String fullIMSI) { 91 if (fullIMSI == null) { 92 return false; 93 } 94 95 if (mPrefix) { 96 // Prefix matching. 97 return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length()); 98 } else { 99 // Exact matching. 100 return mImsi.equals(fullIMSI); 101 } 102 } 103 104 /** 105 * Perform matching against the given MCC-MNC (Mobile Country Code and Mobile Network 106 * Code) combination. 107 * 108 * @param mccMnc The MCC-MNC to match against 109 * @return true if matched 110 */ matchesMccMnc(String mccMnc)111 public boolean matchesMccMnc(String mccMnc) { 112 if (mccMnc == null) { 113 return false; 114 } 115 if (mccMnc.length() != MCC_MNC_LENGTH_5 && mccMnc.length() != MCC_MNC_LENGTH_6) { 116 return false; 117 } 118 if (mPrefix && mccMnc.length() != mImsi.length()) { 119 return false; 120 } 121 122 return mImsi.startsWith(mccMnc); 123 } 124 125 /** 126 * If the IMSI is full length. 127 * 128 * @return true If the length of IMSI is full, false otherwise. 129 */ isFullImsi()130 public boolean isFullImsi() { 131 return !mPrefix; 132 } 133 134 @Override equals(Object thatObject)135 public boolean equals(Object thatObject) { 136 if (this == thatObject) { 137 return true; 138 } 139 if (!(thatObject instanceof IMSIParameter)) { 140 return false; 141 } 142 143 IMSIParameter that = (IMSIParameter) thatObject; 144 return mPrefix == that.mPrefix && TextUtils.equals(mImsi, that.mImsi); 145 } 146 147 @Override hashCode()148 public int hashCode() { 149 int result = mImsi != null ? mImsi.hashCode() : 0; 150 result = 31 * result + (mPrefix ? 1 : 0); 151 return result; 152 } 153 154 @Override toString()155 public String toString() { 156 if (mPrefix) { 157 return mImsi + '*'; 158 } 159 else { 160 return mImsi; 161 } 162 } 163 } 164