1 /* 2 * Copyright (C) 2022 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.internal.telephony; 18 19 import android.text.TextUtils; 20 21 import com.android.i18n.phonenumbers.NumberParseException; 22 import com.android.i18n.phonenumbers.PhoneNumberUtil; 23 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; 24 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.internal.telephony.uicc.AdnRecord; 27 import com.android.internal.telephony.uicc.AdnRecordCache; 28 import com.android.internal.telephony.uicc.IccConstants; 29 import com.android.internal.telephony.uicc.IccRecords; 30 import com.android.internal.telephony.uicc.UiccCardApplication; 31 import com.android.internal.telephony.uicc.UiccController; 32 import com.android.internal.telephony.uicc.UiccProfile; 33 import com.android.telephony.Rlog; 34 35 import java.util.ArrayList; 36 import java.util.regex.PatternSyntaxException; 37 38 /** 39 * This is a basic utility class for common functions related to Fixed Dialing Numbers 40 * designed as per 3GPP 22.101. 41 */ 42 public class FdnUtils { 43 private static final boolean VDBG = false; 44 private static final String LOG_TAG = FdnUtils.class.getSimpleName(); 45 46 /** 47 * The following function checks if dialed number is blocked due to FDN. 48 * 49 * @param phoneId The phone object id for which the FDN check is performed 50 * @param dialStr dialed phone number 51 * @param defaultCountryIso country ISO for the subscription associated with this phone 52 * @return {@code true} if dialStr is blocked due to FDN check. 53 */ isNumberBlockedByFDN(int phoneId, String dialStr, String defaultCountryIso)54 public static boolean isNumberBlockedByFDN(int phoneId, String dialStr, 55 String defaultCountryIso) { 56 if (!isFdnEnabled(phoneId)) { 57 return false; 58 } 59 60 ArrayList<AdnRecord> fdnList = getFdnList(phoneId); 61 return !isFDN(dialStr, defaultCountryIso, fdnList); 62 } 63 64 /** 65 * Checks if FDN is enabled 66 * @param phoneId The phone object id for which the FDN check is performed 67 * @return {@code true} if FDN is enabled 68 */ isFdnEnabled(int phoneId)69 public static boolean isFdnEnabled(int phoneId) { 70 UiccCardApplication app = getUiccCardApplication(phoneId); 71 if (app == null || (!app.getIccFdnAvailable())) { 72 return false; 73 } 74 75 return app.getIccFdnEnabled(); 76 } 77 78 /** 79 * If FDN is enabled, check to see if the given supplementary service control strings are 80 * blocked due to FDN. 81 * @param phoneId The phone object id for which the FDN check is performed 82 * @param controlStrings control strings associated with the supplementary service request 83 * @param defaultCountryIso country ISO for the subscription associated with this phone 84 * @return {@code true} if the FDN list does not contain any of the control strings. 85 */ isSuppServiceRequestBlockedByFdn(int phoneId, ArrayList<String> controlStrings, String defaultCountryIso)86 public static boolean isSuppServiceRequestBlockedByFdn(int phoneId, 87 ArrayList<String> controlStrings, String defaultCountryIso) { 88 if (!isFdnEnabled(phoneId)) { 89 return false; 90 } 91 92 ArrayList<AdnRecord> fdnList = getFdnList(phoneId); 93 for(String controlString : controlStrings) { 94 if(isFDN(controlString, defaultCountryIso, fdnList)) { 95 return false; 96 } 97 } 98 return true; 99 } 100 101 /** 102 * Checks if dialStr is part of FDN list. 103 * 104 * @param fdnList List of all FDN records associated with a sim card 105 * @param dialStr dialed phone number 106 * @param defaultCountryIso country ISO for the subscription associated with this phone 107 * @return {@code true} if dialStr is present in the fdnList. 108 */ 109 @VisibleForTesting isFDN(String dialStr, String defaultCountryIso, ArrayList<AdnRecord> fdnList)110 public static boolean isFDN(String dialStr, String defaultCountryIso, 111 ArrayList<AdnRecord> fdnList) { 112 if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) { 113 Rlog.w(LOG_TAG, "isFDN: unexpected null value"); 114 return false; 115 } 116 117 // Parse the dialStr and convert it to E164 format 118 String dialStrE164 = null; 119 String dialStrNational = null; 120 final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); 121 try { 122 PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso); 123 dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164); 124 dialStrNational = String.valueOf(phoneNumber.getNationalNumber()); 125 } catch (NumberParseException ignored) { 126 Rlog.w(LOG_TAG, "isFDN: could not parse dialStr"); 127 dialStr = extractSMSC(dialStr); 128 } 129 130 /** 131 * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber 132 * E.g.1: returns true if fdnNumber="123" and dialStr="12345" 133 * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345" 134 */ 135 for (AdnRecord fdn: fdnList) { 136 String fdnNumber = fdn.getNumber(); 137 if (TextUtils.isEmpty(fdnNumber)) { 138 continue; 139 } 140 141 if(!TextUtils.isEmpty(dialStrE164)) { 142 if(dialStrE164.startsWith(fdnNumber)) { 143 return true; 144 } 145 } 146 147 if(!TextUtils.isEmpty(dialStrNational)) { 148 if (dialStrNational.startsWith(fdnNumber)) { 149 return true; 150 } 151 } 152 153 if (dialStr.startsWith(fdnNumber)) { 154 return true; 155 } 156 } 157 158 if (VDBG) { 159 Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list"); 160 } 161 return false; 162 } 163 getFdnList(int phoneId)164 private static ArrayList<AdnRecord> getFdnList(int phoneId) { 165 UiccCardApplication app = getUiccCardApplication(phoneId); 166 if (app == null) { 167 return null; 168 } 169 170 IccRecords iccRecords = app.getIccRecords(); 171 if (iccRecords == null) { 172 return null; 173 } 174 175 AdnRecordCache adnRecordCache = iccRecords.getAdnCache(); 176 if(adnRecordCache == null) { 177 return null; 178 } 179 180 return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN); 181 } 182 getUiccCardApplication(int phoneId)183 private static UiccCardApplication getUiccCardApplication(int phoneId) { 184 UiccProfile uiccProfile = UiccController.getInstance() 185 .getUiccProfileForPhone(phoneId); 186 if (uiccProfile == null) { 187 return null; 188 } 189 190 return uiccProfile.getApplication(UiccController.APP_FAM_3GPP); 191 } 192 extractSMSC(String dialStr)193 private static String extractSMSC(String dialStr) { 194 try { 195 String[] dialStrParts = null; 196 if (dialStr.contains(",")) { 197 // SMSC can be in the format of ""+123456789123",123" 198 // Split into two parts using comma as delimiter 199 // and first part of the string is used as smsc address 200 dialStrParts = dialStr.split(","); 201 } else if (dialStr.contains("@")) { 202 // SMSC can be in the format of "+123456789123@ims.mnc.org" 203 // Split into two parts using @ as delimiter 204 // and first part of the string is used as smsc address 205 dialStrParts = dialStr.split("@"); 206 } 207 208 if (dialStrParts != null && dialStrParts.length >= 1) { 209 if (dialStrParts[0].contains("\"")) { 210 // If SMSC is in this format: ""+123456789123",123", after performing above 211 // split we get string with double-quotation marks in it 212 // dialStrParts[0] = ""+123456789123"". 213 // Here, we remove double-quotation marks from the string. 214 dialStrParts[0] = dialStrParts[0].replaceAll("\"", ""); 215 } 216 return dialStrParts[0]; 217 } 218 } catch (PatternSyntaxException ex) { 219 Rlog.w(LOG_TAG, "extractSMSC: Could not extract number from dialStr " + ex); 220 } 221 222 // Return original dialStr if it is not in any of the formats mentions above. 223 return dialStr; 224 } 225 }