Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39ef4f0d authored by Aishwarya Mallampati's avatar Aishwarya Mallampati Committed by Android (Google) Code Review
Browse files

Merge "Added support for FDN Check."

parents 58d6a50a c23e4bbd
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ public class CallStateException extends Exception
    public static final int ERROR_CALLING_DISABLED = 5;
    public static final int ERROR_CALLING_DISABLED = 5;
    public static final int ERROR_TOO_MANY_CALLS = 6;
    public static final int ERROR_TOO_MANY_CALLS = 6;
    public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7;
    public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7;
    public static final int ERROR_FDN_BLOCKED = 8;


    public
    public
    CallStateException()
    CallStateException()
+192 −0
Original line number Original line Diff line number Diff line
/*
 * 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.content.Context;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
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.Locale;

/**
 * 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 true if dialStr is blocked due to FDN check.
     */
    public static boolean isNumberBlockedByFDN(int phoneId, String dialStr,
            String defaultCountryIso) {
        final UiccCardApplication app = getUiccCardApplication(phoneId);

        if (!isFdnEnabled(app)) {
            return false;
        }

        final ArrayList<AdnRecord> fdnList = getFdnList(app);
        return !isFDN(dialStr, defaultCountryIso, fdnList);
    }

    /**
     * The following function checks if destination address or smsc is blocked due to FDN.
     * @param context context
     * @param subId subscription ID
     * @param destAddr destination address of the message
     * @param smscAddr smsc address of the subscription
     * @return true if either destAddr or smscAddr is blocked due to FDN.
     */
    public static boolean isNumberBlockedByFDN(Context context, int subId, String destAddr,
            String smscAddr) {
        // Skip FDN check for emergency numbers
        final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
        if(tm.isEmergencyNumber(destAddr)) {
            return false;
        }

        final int phoneId = SubscriptionManager.getPhoneId(subId);
        final String defaultCountryIso = tm.getSimCountryIso().toUpperCase(Locale.ENGLISH);
        return (isNumberBlockedByFDN(phoneId, smscAddr, defaultCountryIso) ||
                isNumberBlockedByFDN(phoneId, destAddr, defaultCountryIso));
    }

    /**
     * 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 true if dialStr is present in the fdnList.
     */
    @VisibleForTesting
    public static boolean isFDN(String dialStr, String defaultCountryIso,
            ArrayList<AdnRecord> 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 {
            final 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");
        }

        /**
         * 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<AdnRecord> getFdnList(UiccCardApplication app) {
        if (app == null) {
            return null;
        }

        final IccRecords iccRecords = app.getIccRecords();
        if (iccRecords == null) {
            return null;
        }

        final AdnRecordCache adnRecordCache = iccRecords.getAdnCache();
        if(adnRecordCache == null) {
            return null;
        }

        return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN);
    }

    private static boolean isFdnEnabled(UiccCardApplication app) {
        if (app == null) {
            return false;
        }

        if (!app.getIccFdnAvailable()) {
            return false;
        }

        return app.getIccFdnEnabled();
    }

    private static UiccCardApplication getUiccCardApplication(int phoneId) {
        final UiccProfile uiccProfile = UiccController.getInstance()
                .getUiccProfileForPhone(phoneId);
        if (uiccProfile == null) {
            return null;
        }

        return uiccProfile.getApplication(UiccController.APP_FAM_3GPP);
    }
}
 No newline at end of file
+6 −0
Original line number Original line Diff line number Diff line
@@ -1494,6 +1494,12 @@ public class GsmCdmaPhone extends Phone {
                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
        }
        }


        // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN
        if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) {
            throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED,
                    "cannot dial number blocked by FDN");
        }

        // Bypass WiFi Only WFC check if this is an emergency call - we should still try to
        // Bypass WiFi Only WFC check if this is an emergency call - we should still try to
        // place over cellular if possible.
        // place over cellular if possible.
        if (!isEmergency) {
        if (!isEmergency) {
+41 −1
Original line number Original line Diff line number Diff line
@@ -151,6 +151,14 @@ public class SmsController extends ISmsImplBase {
        if (callingPackage == null) {
        if (callingPackage == null) {
            callingPackage = getCallingPackage();
            callingPackage = getCallingPackage();
        }
        }

        // Perform FDN check
        if (FdnUtils.isNumberBlockedByFDN(mContext, subId, destAddr,
                getSmscAddressFromIccEfForSubscriber(subId, callingPackage))) {
            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
            return;
        }

        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        if (iccSmsIntMgr != null) {
        if (iccSmsIntMgr != null) {
            iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort,
            iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort,
@@ -201,6 +209,14 @@ public class SmsController extends ISmsImplBase {
        } finally {
        } finally {
            Binder.restoreCallingIdentity(token);
            Binder.restoreCallingIdentity(token);
        }
        }

        // Perform FDN check
        if (FdnUtils.isNumberBlockedByFDN(mContext, subId, destAddr,
                getSmscAddressFromIccEfForSubscriber(subId, callingPackage))) {
            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
            return;
        }

        if (isBluetoothSubscription(info)) {
        if (isBluetoothSubscription(info)) {
            sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
            sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
        } else {
        } else {
@@ -259,6 +275,14 @@ public class SmsController extends ISmsImplBase {
        if (callingPackage == null) {
        if (callingPackage == null) {
            callingPackage = getCallingPackage();
            callingPackage = getCallingPackage();
        }
        }

        // Perform FDN check
        if (FdnUtils.isNumberBlockedByFDN(mContext, subId, destAddr,
                getSmscAddressFromIccEfForSubscriber(subId, callingPackage))) {
            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
            return;
        }

        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        if (iccSmsIntMgr != null) {
        if (iccSmsIntMgr != null) {
            iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr,
            iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr,
@@ -281,6 +305,14 @@ public class SmsController extends ISmsImplBase {
        if (getCallingPackage() != null) {
        if (getCallingPackage() != null) {
            callingPackage = getCallingPackage();
            callingPackage = getCallingPackage();
        }
        }

        // Perform FDN check
        if (FdnUtils.isNumberBlockedByFDN(mContext, subId, destAddr,
                getSmscAddressFromIccEfForSubscriber(subId, callingPackage))) {
            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
            return;
        }

        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        if (iccSmsIntMgr != null) {
        if (iccSmsIntMgr != null) {
            iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr,
            iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr,
@@ -301,6 +333,14 @@ public class SmsController extends ISmsImplBase {
        if (callingPackage == null) {
        if (callingPackage == null) {
            callingPackage = getCallingPackage();
            callingPackage = getCallingPackage();
        }
        }

        // Perform FDN check
        if (FdnUtils.isNumberBlockedByFDN(mContext, subId, destAddr,
                getSmscAddressFromIccEfForSubscriber(subId, callingPackage))) {
            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE);
            return;
        }

        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
        if (iccSmsIntMgr != null) {
        if (iccSmsIntMgr != null) {
            iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag,
            iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag,
+166 −0
Original line number Original line Diff line number Diff line
/*
 * 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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import com.android.internal.telephony.uicc.AdnRecord;

import org.junit.Test;

import java.util.ArrayList;

public class FdnUtilsTest {

    private ArrayList<AdnRecord> initializeFdnList() {
        ArrayList<AdnRecord> fdnList = new ArrayList<>();
        AdnRecord adnRecord = new AdnRecord(null, null);
        // By default, every sim card holds 15 empty FDN records
        int fdnListSize = 15;
        for (int i = 0; i < fdnListSize; i++) {
            fdnList.add(adnRecord);
        }
        return fdnList;
    }

    @Test
    public void fdnListIsNull_returnsFalse() {
        assertFalse(FdnUtils.isFDN( "123456789", "US", null));
    }

    @Test
    public void fdnListIsEmpty_returnsFalse() {
        ArrayList<AdnRecord> fdnList = new ArrayList<>();
        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
    }

    @Test
    public void fdnListHasOnlyDefaultRecords_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();

        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
    }

    @Test
    public void fdnListHasRecordWithEmptyNumberStr_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "");
        fdnList.add(1, adnRecord);

        assertFalse(FdnUtils.isFDN( "123456789", "US", fdnList));
    }

    @Test
    public void dialStrInFdnList_returnsTrue() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "123456789");
        fdnList.add(2, adnRecord);

        assertTrue(FdnUtils.isFDN( "123456789", "US", fdnList));
    }

    @Test
    public void dialStrNotInFdnList_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "111111111");
        fdnList.add(3, adnRecord);

        assertFalse(FdnUtils.isFDN("123456788", "US", fdnList));
    }

    @Test
    public void dialStrIsNull_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "111111111");
        fdnList.add(4, adnRecord);

        assertFalse(FdnUtils.isFDN( null, "US", fdnList));
    }

    @Test
    public void fdnEntryFirstSubStringOfDialStr_returnsTrue() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "123");
        fdnList.add(5, adnRecord);

        assertTrue(FdnUtils.isFDN( "12345", "US", fdnList));
    }

    @Test
    public void fdnEntrySubStringOfDialStr_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "123");
        fdnList.add(5, adnRecord);

        assertFalse(FdnUtils.isFDN("612345", "US", fdnList));
    }

    @Test
    public void dialStrFirstSubStringOfFdnEntry_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "12345");
        fdnList.add(5, adnRecord);

        assertFalse(FdnUtils.isFDN("123", "US", fdnList));
    }

    @Test
    public void dialStrSubStringOfFdnEntry_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "612345");
        fdnList.add(5, adnRecord);

        assertFalse(FdnUtils.isFDN("123", "US", fdnList));
    }

    @Test
    public void dialStrWithoutCountryCode_returnsTrue() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "+16502910000");
        fdnList.add(6, adnRecord);

        assertTrue(FdnUtils.isFDN( "6502910000", "US", fdnList));
    }

    @Test
    public void dialStrWithCountryCode_returnsTrue() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "6502910000");
        fdnList.add(6, adnRecord);

        assertTrue(FdnUtils.isFDN("+16502910000", "US", fdnList));
    }

    @Test
    public void defaultCountryIsoIsEmpty_returnsTrue() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "650");
        fdnList.add(6, adnRecord);

        assertTrue(FdnUtils.isFDN("+16502910000", "", fdnList));
    }

    @Test
    public void defaultCountryIsoIsEmpty_returnsFalse() {
        ArrayList<AdnRecord> fdnList = initializeFdnList();
        AdnRecord adnRecord = new AdnRecord(null, "+1650");
        fdnList.add(6, adnRecord);

        assertFalse(FdnUtils.isFDN("6502910000", "", fdnList));
    }
}
 No newline at end of file