/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.text.TextUtils; import com.android.i18n.phonenumbers.NumberParseException; import com.android.i18n.phonenumbers.PhoneNumberUtil; import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.uicc.AdnRecord; import com.android.internal.telephony.uicc.AdnRecordCache; import com.android.internal.telephony.uicc.IccConstants; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccProfile; import com.android.telephony.Rlog; import java.util.ArrayList; import java.util.regex.PatternSyntaxException; /** * This is a basic utility class for common functions related to Fixed Dialing Numbers * designed as per 3GPP 22.101. */ public class FdnUtils { private static final boolean VDBG = false; private static final String LOG_TAG = FdnUtils.class.getSimpleName(); /** * The following function checks if dialed number is blocked due to FDN. * * @param phoneId The phone object id for which the FDN check is performed * @param dialStr dialed phone number * @param defaultCountryIso country ISO for the subscription associated with this phone * @return {@code true} if dialStr is blocked due to FDN check. */ public static boolean isNumberBlockedByFDN(int phoneId, String dialStr, String defaultCountryIso) { if (!isFdnEnabled(phoneId)) { return false; } ArrayList fdnList = getFdnList(phoneId); return !isFDN(dialStr, defaultCountryIso, fdnList); } /** * Checks if FDN is enabled * @param phoneId The phone object id for which the FDN check is performed * @return {@code true} if FDN is enabled */ public static boolean isFdnEnabled(int phoneId) { UiccCardApplication app = getUiccCardApplication(phoneId); if (app == null || (!app.getIccFdnAvailable())) { return false; } return app.getIccFdnEnabled(); } /** * If FDN is enabled, check to see if the given supplementary service control strings are * blocked due to FDN. * @param phoneId The phone object id for which the FDN check is performed * @param controlStrings control strings associated with the supplementary service request * @param defaultCountryIso country ISO for the subscription associated with this phone * @return {@code true} if the FDN list does not contain any of the control strings. */ public static boolean isSuppServiceRequestBlockedByFdn(int phoneId, ArrayList controlStrings, String defaultCountryIso) { if (!isFdnEnabled(phoneId)) { return false; } ArrayList fdnList = getFdnList(phoneId); for(String controlString : controlStrings) { if(isFDN(controlString, defaultCountryIso, fdnList)) { return false; } } return true; } /** * Checks if dialStr is part of FDN list. * * @param fdnList List of all FDN records associated with a sim card * @param dialStr dialed phone number * @param defaultCountryIso country ISO for the subscription associated with this phone * @return {@code true} if dialStr is present in the fdnList. */ @VisibleForTesting public static boolean isFDN(String dialStr, String defaultCountryIso, ArrayList fdnList) { if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) { Rlog.w(LOG_TAG, "isFDN: unexpected null value"); return false; } // Parse the dialStr and convert it to E164 format String dialStrE164 = null; String dialStrNational = null; final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); try { PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso); dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164); dialStrNational = String.valueOf(phoneNumber.getNationalNumber()); } catch (NumberParseException ignored) { Rlog.w(LOG_TAG, "isFDN: could not parse dialStr"); dialStr = extractSMSC(dialStr); } /** * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber * E.g.1: returns true if fdnNumber="123" and dialStr="12345" * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345" */ for (AdnRecord fdn: fdnList) { String fdnNumber = fdn.getNumber(); if (TextUtils.isEmpty(fdnNumber)) { continue; } if(!TextUtils.isEmpty(dialStrE164)) { if(dialStrE164.startsWith(fdnNumber)) { return true; } } if(!TextUtils.isEmpty(dialStrNational)) { if (dialStrNational.startsWith(fdnNumber)) { return true; } } if (dialStr.startsWith(fdnNumber)) { return true; } } if (VDBG) { Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list"); } return false; } private static ArrayList getFdnList(int phoneId) { UiccCardApplication app = getUiccCardApplication(phoneId); if (app == null) { return null; } IccRecords iccRecords = app.getIccRecords(); if (iccRecords == null) { return null; } AdnRecordCache adnRecordCache = iccRecords.getAdnCache(); if(adnRecordCache == null) { return null; } return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN); } private static UiccCardApplication getUiccCardApplication(int phoneId) { UiccProfile uiccProfile = UiccController.getInstance() .getUiccProfileForPhone(phoneId); if (uiccProfile == null) { return null; } return uiccProfile.getApplication(UiccController.APP_FAM_3GPP); } private static String extractSMSC(String dialStr) { try { String[] dialStrParts = null; if (dialStr.contains(",")) { // SMSC can be in the format of ""+123456789123",123" // Split into two parts using comma as delimiter // and first part of the string is used as smsc address dialStrParts = dialStr.split(","); } else if (dialStr.contains("@")) { // SMSC can be in the format of "+123456789123@ims.mnc.org" // Split into two parts using @ as delimiter // and first part of the string is used as smsc address dialStrParts = dialStr.split("@"); } if (dialStrParts != null && dialStrParts.length >= 1) { if (dialStrParts[0].contains("\"")) { // If SMSC is in this format: ""+123456789123",123", after performing above // split we get string with double-quotation marks in it // dialStrParts[0] = ""+123456789123"". // Here, we remove double-quotation marks from the string. dialStrParts[0] = dialStrParts[0].replaceAll("\"", ""); } return dialStrParts[0]; } } catch (PatternSyntaxException ex) { Rlog.w(LOG_TAG, "extractSMSC: Could not extract number from dialStr " + ex); } // Return original dialStr if it is not in any of the formats mentions above. return dialStr; } }