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

Commit 63923b92 authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Support Emergency calling in CTS

1) Move system dialer query code into DefaultDialerCache in
preparation to be able to override it for testing.
2) Implement test emergency phone account package name filter,
which restricts test emergency numbers to being dialed over
specific test connection services. For safety, real emergency
numbers will ignore this filter.

Bug: 138741228
Test: atest CtsTelecomTestCases; atest TelecomUnitTests
Change-Id: Iade1b4d704f86af0898c166c8f6369555cea9713
parent 6f0d9949
Loading
Loading
Loading
Loading
+34 −2
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.StatsLog;
import android.os.UserHandle;
@@ -62,11 +64,13 @@ import java.io.IOException;
import java.lang.String;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -336,6 +340,10 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,

    private boolean mIsEmergencyCall;

    // The Call is considered an emergency call for testing, but will not actually connect to
    // emergency services.
    private boolean mIsTestEmergencyCall;

    private boolean mSpeakerphoneOn;

    private boolean mIsDisconnectingChildCall = false;
@@ -1072,10 +1080,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            // call, it will remain so for the rest of it's lifetime.
            if (!mIsEmergencyCall) {
                mIsEmergencyCall = mHandle != null &&
                        mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
                                mHandle.getSchemeSpecificPart());
                        getTelephonyManager().isEmergencyNumber(mHandle.getSchemeSpecificPart());
                mAnalytics.setCallIsEmergency(mIsEmergencyCall);
            }
            if (!mIsTestEmergencyCall) {
                mIsTestEmergencyCall = mHandle != null &&
                        isTestEmergencyCall(mHandle.getSchemeSpecificPart());
            }
            startCallerInfoLookup();
            for (Listener l : mListeners) {
                l.onHandleChanged(this);
@@ -1083,6 +1094,14 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
    }

    private boolean isTestEmergencyCall(String number) {
        Map<Integer, List<EmergencyNumber>> eMap = getTelephonyManager().getEmergencyNumberList();
        return eMap.values().stream().flatMap(Collection::stream)
                .anyMatch(eNumber ->
                        eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) &&
                                number.equals(eNumber.getNumber()));
    }

    public String getCallerDisplayName() {
        return mCallerDisplayName;
    }
@@ -1142,6 +1161,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return mIsEmergencyCall;
    }

    /**
     * @return {@code true} if this an outgoing call to a test emergency number (and NOT to
     * emergency services). Used for testing purposes to differentiate between a real and fake
     * emergency call for safety reasons during testing.
     */
    public boolean isTestEmergencyCall() {
        return mIsTestEmergencyCall;
    }

    /**
     * @return {@code true} if the network has identified this call as an emergency call.
     */
@@ -3161,6 +3189,10 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
    }

    private TelephonyManager getTelephonyManager() {
        return mContext.getSystemService(TelephonyManager.class);
    }

    /**
     * Sets whether this {@link Call} is a conference or not.
     * @param isConference
+21 −14
Original line number Diff line number Diff line
@@ -38,11 +38,16 @@ public class CallIntentProcessor {
    }

    public static class AdapterImpl implements Adapter {
        private final DefaultDialerCache mDefaultDialerCache;
        public AdapterImpl(DefaultDialerCache cache) {
            mDefaultDialerCache = cache;
        }

        @Override
        public void processOutgoingCallIntent(Context context, CallsManager callsManager,
                Intent intent, String callingPackage) {
            CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent,
                    callingPackage);
                    callingPackage, mDefaultDialerCache);
        }

        @Override
@@ -58,11 +63,6 @@ public class CallIntentProcessor {

    public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
    public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
    /*
     *  Whether or not the dialer initiating this outgoing call is the default dialer, or system
     *  dialer and thus allowed to make emergency calls.
     */
    public static final String KEY_IS_PRIVILEGED_DIALER = "is_privileged_dialer";

    /**
     * The user initiating the outgoing call.
@@ -72,10 +72,13 @@ public class CallIntentProcessor {

    private final Context mContext;
    private final CallsManager mCallsManager;
    private final DefaultDialerCache mDefaultDialerCache;

    public CallIntentProcessor(Context context, CallsManager callsManager) {
    public CallIntentProcessor(Context context, CallsManager callsManager,
            DefaultDialerCache defaultDialerCache) {
        this.mContext = context;
        this.mCallsManager = callsManager;
        this.mDefaultDialerCache = defaultDialerCache;
    }

    public void processIntent(Intent intent, String callingPackage) {
@@ -86,7 +89,8 @@ public class CallIntentProcessor {
        if (isUnknownCall) {
            processUnknownCallIntent(mCallsManager, intent);
        } else {
            processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage);
            processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage,
                    mDefaultDialerCache);
        }
        Trace.endSection();
    }
@@ -102,7 +106,8 @@ public class CallIntentProcessor {
            Context context,
            CallsManager callsManager,
            Intent intent,
            String callingPackage) {
            String callingPackage,
            DefaultDialerCache defaultDialerCache) {

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
@@ -157,6 +162,9 @@ public class CallIntentProcessor {

        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);

        boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
                initiatingUser.getIdentifier());

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        CompletableFuture<Call> callFuture = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
@@ -167,7 +175,8 @@ public class CallIntentProcessor {
            if (call != null) {
                Log.continueSession(logSubsession, "CIP.sNOCI");
                try {
                    sendNewOutgoingCallIntent(context, call, callsManager, intent);
                    sendNewOutgoingCallIntent(context, call, callsManager, intent,
                            isPrivilegedDialer, defaultDialerCache);
                } finally {
                    Log.endSession();
                }
@@ -176,17 +185,15 @@ public class CallIntentProcessor {
    }

    static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
            Intent intent) {
            Intent intent, boolean isPrivilegedDialer, DefaultDialerCache defaultDialerCache) {
        // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
        // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
        // killed if memory is scarce. However, this is OK here because the entire Telecom
        // process will be running throughout the duration of the phone call and should never
        // be killed.
        final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);

        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer);
                isPrivilegedDialer, defaultDialerCache);

        // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
        NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
+7 −4
Original line number Diff line number Diff line
@@ -1623,6 +1623,7 @@ public class CallsManager extends Call.ListenerBase
     * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching
     *               phone accounts.
     * @param isVideo {@code true} if the call is a video call, {@code false} otherwise.
     * @param isEmergency {@code true} if the call is an emergency call.
     * @param initiatingUser The {@link UserHandle} the call is placed on.
     * @return
     */
@@ -1753,7 +1754,7 @@ public class CallsManager extends Call.ListenerBase
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
            endEarly = true;
            disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
        } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext,
        } else if (getTelephonyManager().isPotentialEmergencyNumber(
                handle.getSchemeSpecificPart())) {
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
                    + " Redirection Service", handle.getSchemeSpecificPart());
@@ -2225,10 +2226,8 @@ public class CallsManager extends Call.ListenerBase
                        isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY);
        // First check the Radio SIM Technology
        if(mRadioSimVariants == null) {
            TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
                    Context.TELEPHONY_SERVICE);
            // Cache Sim Variants
            mRadioSimVariants = tm.getMultiSimConfiguration();
            mRadioSimVariants = getTelephonyManager().getMultiSimConfiguration();
        }
        // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
        // Should be available if a call is already active on the SIM account.
@@ -2252,6 +2251,10 @@ public class CallsManager extends Call.ListenerBase
        return allAccounts;
    }

    private TelephonyManager getTelephonyManager() {
        return mContext.getSystemService(TelephonyManager.class);
    }

    /**
     * Informs listeners (notably {@link CallAudioManager} of a change to the call's external
     * property.
+6 −0
Original line number Diff line number Diff line
@@ -383,6 +383,12 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
                allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
            }

            // When testing emergency calls, we want the calls to go through to the test connection
            // service, not the telephony ConnectionService.
            if (mCall.isTestEmergencyCall()) {
                allAccounts = mPhoneAccountRegistrar.filterRestrictedPhoneAccounts(allAccounts);
            }

            // Get user preferred PA if it exists.
            PhoneAccount preferredPA = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
                    preferredPAH);
+26 −3
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.server.telecom;

import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
@@ -134,9 +136,10 @@ public class DefaultDialerCache {
    private final Context mContext;
    private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
    private final TelecomSystem.SyncRoot mLock;
    private final String mSystemDialerName;
    private final ComponentName mSystemDialerComponentName;
    private final RoleManagerAdapter mRoleManagerAdapter;
    private SparseArray<String> mCurrentDefaultDialerPerUser = new SparseArray<>();
    private ComponentName mOverrideSystemDialerComponentName;

    public DefaultDialerCache(Context context,
            DefaultDialerManagerAdapter defaultDialerManagerAdapter,
@@ -146,7 +149,11 @@ public class DefaultDialerCache {
        mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
        mRoleManagerAdapter = roleManagerAdapter;
        mLock = lock;
        mSystemDialerName = TelecomServiceImpl.getSystemDialerPackage(mContext);
        Resources resources = mContext.getResources();
        mSystemDialerComponentName = new ComponentName(resources.getString(
                com.android.internal.R.string.config_defaultDialer),
                resources.getString(R.string.incall_default_class));


        IntentFilter packageIntentFilter = new IntentFilter();
        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
@@ -194,6 +201,22 @@ public class DefaultDialerCache {
        return getDefaultDialerApplication(mContext.getUserId());
    }

    public void setSystemDialerComponentName(ComponentName testComponentName) {
        mOverrideSystemDialerComponentName = testComponentName;
    }

    public String getSystemDialerApplication() {
        if (mOverrideSystemDialerComponentName != null) {
            return mOverrideSystemDialerComponentName.getPackageName();
        }
        return mSystemDialerComponentName.getPackageName();
    }

    public ComponentName getSystemDialerComponent() {
        if (mOverrideSystemDialerComponentName != null) return mOverrideSystemDialerComponentName;
        return mSystemDialerComponentName;
    }

    public void observeDefaultDialerApplication(Executor executor, IntConsumer observer) {
        mRoleManagerAdapter.observeDefaultDialerApp(executor, observer);
    }
@@ -201,7 +224,7 @@ public class DefaultDialerCache {
    public boolean isDefaultOrSystemDialer(String packageName, int userId) {
        String defaultDialer = getDefaultDialerApplication(userId);
        return Objects.equals(packageName, defaultDialer)
                || Objects.equals(packageName, mSystemDialerName);
                || Objects.equals(packageName, getSystemDialerApplication());
    }

    public boolean setDefaultDialer(String packageName, int userId) {
Loading