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

Commit 0e3fe4c1 authored by Hunsuk Choi's avatar Hunsuk Choi Committed by Android (Google) Code Review
Browse files

Merge "Send ACTION_EMERGENCY_CALL_STATE_CHANGED" into 24D1-dev

parents 87ae3a5d d3e2d891
Loading
Loading
Loading
Loading
+69 −13
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.internal.telephony.emergency;
import static android.telecom.Connection.STATE_ACTIVE;
import static android.telecom.Connection.STATE_DISCONNECTED;
import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL;

import static com.android.internal.telephony.TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED;
import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE;
import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN;
@@ -159,6 +161,7 @@ public class EmergencyStateTracker {
    private boolean mIsTestEmergencyNumber;
    private Runnable mOnEcmExitCompleteRunnable;
    private int mOngoingCallProperties;
    private boolean mSentEmergencyCallState;

    /** For emergency SMS */
    private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>();
@@ -174,6 +177,8 @@ public class EmergencyStateTracker {

    private final android.util.ArrayMap<Integer, Boolean> mNoSimEcbmSupported =
            new android.util.ArrayMap<>();
    private final android.util.ArrayMap<Integer, Boolean> mBroadcastEmergencyCallStateChanges =
            new android.util.ArrayMap<>();
    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
                    slotIndex, subId);
@@ -466,6 +471,9 @@ public class EmergencyStateTracker {
        mTelephonyManagerProxy = new TelephonyManagerProxyImpl(context);

        registerForNewRingingConnection();

        // To recover the abnormal state after crash of com.android.phone process
        maybeResetEmergencyCallStateChangedIntent();
    }

    /**
@@ -581,6 +589,7 @@ public class EmergencyStateTracker {
                mPhone = phone;
                mOngoingConnection = c;
                mIsTestEmergencyNumber = isTestEmergencyNumber;
                sendEmergencyCallStateChange(mPhone, true);
                maybeRejectIncomingCall(null);
                return mCallEmergencyModeFuture;
            }
@@ -589,6 +598,7 @@ public class EmergencyStateTracker {
        mPhone = phone;
        mOngoingConnection = c;
        mIsTestEmergencyNumber = isTestEmergencyNumber;
        sendEmergencyCallStateChange(mPhone, true);
        maybeRejectIncomingCall(result -> {
            Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result);
            turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber);
@@ -611,6 +621,7 @@ public class EmergencyStateTracker {
        if (Objects.equals(mOngoingConnection, c)) {
            mOngoingConnection = null;
            mOngoingCallProperties = 0;
            sendEmergencyCallStateChange(mPhone, false);
        }

        if (wasActive && mActiveEmergencyCalls.isEmpty()
@@ -1199,6 +1210,18 @@ public class EmergencyStateTracker {
                && mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_CS && isInEcm();
    }

    private void sendEmergencyCallStateChange(Phone phone, boolean isAlive) {
        if ((isAlive && !mSentEmergencyCallState && getBroadcastEmergencyCallStateChanges(phone))
                || (!isAlive && mSentEmergencyCallState)) {
            mSentEmergencyCallState = isAlive;
            Rlog.i(TAG, "sendEmergencyCallStateChange: " + isAlive);
            Intent intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED);
            intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, isAlive);
            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phone.getPhoneId());
            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
        }
    }

    /**
     * Starts the process of an emergency SMS.
     *
@@ -1654,9 +1677,9 @@ public class EmergencyStateTracker {
    private boolean getConfig(int subId, String key, boolean defVal) {
        return getConfigBundle(subId, key).getBoolean(key, defVal);
    }
    private PersistableBundle getConfigBundle(int subId, String key) {
    private PersistableBundle getConfigBundle(int subId, String... keys) {
        if (mConfigManager == null) return new PersistableBundle();
        return mConfigManager.getConfigForSubId(subId, key);
        return mConfigManager.getConfigForSubId(subId, keys);
    }

    /**
@@ -1710,10 +1733,6 @@ public class EmergencyStateTracker {
            return;
        }

        updateNoSimEcbmSupported(slotIndex, subId);
    }

    private void updateNoSimEcbmSupported(int slotIndex, int subId) {
        SharedPreferences sp = null;
        Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(slotIndex));
        if (savedConfig == null) {
@@ -1721,8 +1740,8 @@ public class EmergencyStateTracker {
            savedConfig = Boolean.valueOf(
                    sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, false));
            mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), savedConfig);
            Rlog.i(TAG, "updateNoSimEcbmSupported load from preference slotIndex=" + slotIndex
                    + ", supported=" + savedConfig);
            Rlog.i(TAG, "onCarrierConfigChanged load from preference slotIndex=" + slotIndex
                    + ", ecbmSupported=" + savedConfig);
        }

        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -1730,17 +1749,28 @@ public class EmergencyStateTracker {
            return;
        }

        PersistableBundle b = getConfigBundle(subId, KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
        PersistableBundle b = getConfigBundle(subId,
                KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
                KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
        if (b.isEmpty()) {
            Rlog.e(TAG, "updateNoSimEcbmSupported empty result");
            Rlog.e(TAG, "onCarrierConfigChanged empty result");
            return;
        }

        if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
            Rlog.i(TAG, "updateNoSimEcbmSupported not carrier specific configuration");
            Rlog.i(TAG, "onCarrierConfigChanged not carrier specific configuration");
            return;
        }

        // KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL
        boolean broadcast = b.getBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
        mBroadcastEmergencyCallStateChanges.put(
                Integer.valueOf(slotIndex), Boolean.valueOf(broadcast));

        Rlog.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex
                + ", broadcastEmergencyCallStateChanges=" + broadcast);

        // KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL
        boolean carrierConfig = b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
        if (carrierConfig == savedConfig) {
            return;
@@ -1755,8 +1785,34 @@ public class EmergencyStateTracker {
        editor.putBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, carrierConfig);
        editor.apply();

        Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
                + ", supported=" + carrierConfig);
        Rlog.i(TAG, "onCarrierConfigChanged preference updated slotIndex=" + slotIndex
                + ", ecbmSupported=" + carrierConfig);
    }

    private boolean getBroadcastEmergencyCallStateChanges(Phone phone) {
        Boolean broadcast = mBroadcastEmergencyCallStateChanges.get(
                Integer.valueOf(phone.getPhoneId()));
        return (broadcast == null) ? false : broadcast;
    }

    /**
     * Resets the emergency call state if it's in alive state.
     */
    @VisibleForTesting
    public void maybeResetEmergencyCallStateChangedIntent() {
        Intent intent = mContext.registerReceiver(null,
            new IntentFilter(ACTION_EMERGENCY_CALL_STATE_CHANGED), Context.RECEIVER_NOT_EXPORTED);
        if (intent != null
                && ACTION_EMERGENCY_CALL_STATE_CHANGED.equals(intent.getAction())) {
            boolean isAlive = intent.getBooleanExtra(
                    TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
            Rlog.i(TAG, "maybeResetEmergencyCallStateChangedIntent isAlive=" + isAlive);
            if (isAlive) {
                intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED);
                intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
            }
        }
    }

    /**
+162 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyVararg;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -49,10 +50,12 @@ import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
@@ -74,6 +77,7 @@ import com.android.internal.telephony.Connection;
import com.android.internal.telephony.GsmCdmaPhone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.data.PhoneSwitcher;

@@ -2326,6 +2330,8 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
                false /* isRadioOn */);
        when(phone.getSubId()).thenReturn(1);
        setEcmSupportedConfig(phone, true);
        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(phone.getSubId());
        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());

        EmergencyStateTracker testEst = setupEmergencyStateTracker(
                false /* isSuplDdsSwitchRequiredForEmergencyCall */);
@@ -2675,6 +2681,162 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        }
    }

    /**
     * Test that emergency call state changes are sent.
     */
    @Test
    @SmallTest
    public void testSendEmergencyCallStateChanges() {
        mContextFixture.getCarrierConfigBundle().putBoolean(
                CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        // Create test Phone
        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
                /* isRadioOn= */ true);
        when(testPhone.getSubId()).thenReturn(1);
        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
        CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
                .getSystemService(Context.CARRIER_CONFIG_SERVICE);

        verify(cfgManager).registerCarrierConfigChangeListener(any(),
                listenerArgumentCaptor.capture());

        CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
                listenerArgumentCaptor.getAllValues().get(0);

        assertNotNull(carrierConfigChangeListener);

        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(testPhone.getSubId());
        bundle.putBoolean(CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL,
                true);
        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
        // onCarrierConfigChanged with valid subscription
        carrierConfigChangeListener.onCarrierConfigChanged(
                testPhone.getPhoneId(), testPhone.getSubId(),
                TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);

        // Start emergency call
        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
                mTestConnection1, false);

        // Verify intent is sent that emergency call state is changed
        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).sendStickyBroadcastAsUser(
                intentCaptor.capture(), eq(UserHandle.ALL));
        Intent intent = intentCaptor.getValue();
        assertNotNull(intent);
        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
        assertTrue(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, true));

        // End emergency call
        emergencyStateTracker.endCall(mTestConnection1);

        // Verify intent is sent that emergency call state is changed
        verify(mContext, times(2)).sendStickyBroadcastAsUser(
                intentCaptor.capture(), eq(UserHandle.ALL));
        intent = intentCaptor.getValue();
        assertNotNull(intent);
        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
        assertFalse(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false));
    }

    /**
     * Test that emergency call state change is reset after crash.
     */
    @Test
    @SmallTest
    public void testResetEmergencyCallStateChanges() {
        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, true);
        doReturn(intent).when(mContext).registerReceiver(eq(null), any(),
                eq(Context.RECEIVER_NOT_EXPORTED));
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();

        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);

        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
                eq(Context.RECEIVER_NOT_EXPORTED));

        IntentFilter filter = filterCaptor.getValue();

        assertNotNull(filter);
        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));

        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);

        // Verify intent is sent that emergency call state is changed
        verify(mContext).sendStickyBroadcastAsUser(
                intentCaptor.capture(), eq(UserHandle.ALL));
        intent = intentCaptor.getValue();
        assertNotNull(intent);
        assertEquals(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED, intent.getAction());
        assertFalse(intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false));
    }

    /**
     * Test that emergency call state is not reset after crash
     * if it's already reset.
     */
    @Test
    @SmallTest
    public void testResetEmergencyCallStateChangesAlreadyReset() {
        Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false);
        doReturn(intent).when(mContext).registerReceiver(eq(null), any(),
                eq(Context.RECEIVER_NOT_EXPORTED));
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();

        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);

        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
                eq(Context.RECEIVER_NOT_EXPORTED));

        IntentFilter filter = filterCaptor.getValue();

        assertNotNull(filter);
        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));

        // Verify intent is not sent.
        verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
    }

    /**
     * Test that emergency call state is not reset after crash
     * if it has never been sent.
     */
    @Test
    @SmallTest
    public void testResetEmergencyCallStateChangesNotSent() {
        doReturn(null).when(mContext).registerReceiver(eq(null), any(),
                eq(Context.RECEIVER_NOT_EXPORTED));
        // Setup EmergencyStateTracker
        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
        emergencyStateTracker.maybeResetEmergencyCallStateChangedIntent();

        ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class);

        verify(mContext).registerReceiver(eq(null), filterCaptor.capture(),
                eq(Context.RECEIVER_NOT_EXPORTED));

        IntentFilter filter = filterCaptor.getValue();

        assertNotNull(filter);
        assertTrue(filter.hasAction(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED));

        // Verify intent is not sent.
        verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
    }

    private EmergencyStateTracker setupEmergencyStateTracker(
            boolean isSuplDdsSwitchRequiredForEmergencyCall) {
        doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();