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

Commit f61ebe61 authored by Hunter Knepshield's avatar Hunter Knepshield Committed by Sarah Chin
Browse files

Telecom plumbing for SIM call manager voice status

When PhoneAccountRegistrar sees a PhoneAccount with
CAPABILITY_VOICE_CALLING_AVAILABLE registered, it will inform telephony
on the corresponding subId(s) for propagation to ServiceState.

Bug: 205737545
Test: com.android.server.telecom.tests.PhoneAccountRegistrarTest
Change-Id: Ia5894e6ac060975b6e1bd9b78e839e05361e3687
Merged-In: Ia5894e6ac060975b6e1bd9b78e839e05361e3687
parent ec8384e3
Loading
Loading
Loading
Loading
+97 −0
Original line number Original line Diff line number Diff line
@@ -453,6 +453,31 @@ public class PhoneAccountRegistrar {
        return retval;
        return retval;
    }
    }


    /**
     * Loops through all SIM accounts ({@link #getSimPhoneAccounts}) and returns those with SIM call
     * manager components specified in carrier config that match {@code simCallManagerHandle}.
     *
     * <p>Note that this will return handles even when {@code simCallManagerHandle} has not yet been
     * registered or was recently unregistered.
     *
     * <p>If the given {@code simCallManagerHandle} is not the SIM call manager for any active SIMs,
     * returns an empty list.
     */
    public @NonNull List<PhoneAccountHandle> getSimPhoneAccountsFromSimCallManager(
            @NonNull PhoneAccountHandle simCallManagerHandle) {
        List<PhoneAccountHandle> matchingSimHandles = new ArrayList<>();
        for (PhoneAccountHandle simHandle :
                getSimPhoneAccounts(simCallManagerHandle.getUserHandle())) {
            ComponentName simCallManager =
                    getSystemSimCallManagerComponent(getSubscriptionIdForPhoneAccount(simHandle));
            if (simCallManager == null) continue;
            if (simCallManager.equals(simCallManagerHandle.getComponentName())) {
                matchingSimHandles.add(simHandle);
            }
        }
        return matchingSimHandles;
    }

    /**
    /**
     * Sets a filter for which {@link PhoneAccount}s will be returned from
     * Sets a filter for which {@link PhoneAccount}s will be returned from
     * {@link #filterRestrictedPhoneAccounts(List)}. If non-null, only {@link PhoneAccount}s
     * {@link #filterRestrictedPhoneAccounts(List)}. If non-null, only {@link PhoneAccount}s
@@ -866,6 +891,9 @@ public class PhoneAccountRegistrar {
        } else {
        } else {
            fireAccountChanged(account);
            fireAccountChanged(account);
        }
        }
        // If this is the SIM call manager, tell telephony when the voice ServiceState override
        // needs to be updated.
        maybeNotifyTelephonyForVoiceServiceState(account, /* registered= */ true);
    }
    }


    public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
    public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
@@ -875,6 +903,9 @@ public class PhoneAccountRegistrar {
                write();
                write();
                fireAccountsChanged();
                fireAccountsChanged();
                fireAccountUnRegistered(accountHandle);
                fireAccountUnRegistered(accountHandle);
                // If this is the SIM call manager, tell telephony when the voice ServiceState
                // override needs to be updated.
                maybeNotifyTelephonyForVoiceServiceState(account, /* registered= */ false);
            }
            }
        }
        }
    }
    }
@@ -1017,6 +1048,72 @@ public class PhoneAccountRegistrar {
        }
        }
    }
    }


    private void maybeNotifyTelephonyForVoiceServiceState(
            @NonNull PhoneAccount account, boolean registered) {
        // TODO(b/215419665) what about SIM_SUBSCRIPTION accounts? They could theoretically also use
        // these capabilities, but don't today. If they do start using them, then there will need to
        // be a kind of "or" logic between SIM_SUBSCRIPTION and CONNECTION_MANAGER accounts to get
        // the correct value of hasService for a given SIM.
        boolean hasService = false;
        List<PhoneAccountHandle> simHandlesToNotify;
        if (account.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
            // When we unregister the SIM call manager account, we always set hasService back to
            // false since it is no longer providing OTT calling capability once unregistered.
            if (registered) {
                // Note: we do *not* early return when the SUPPORTS capability is not present
                // because it's possible the SIM call manager could remove either capability at
                // runtime and re-register. However, it is an error to use the AVAILABLE capability
                // without also setting SUPPORTS.
                hasService =
                        account.hasCapabilities(
                                PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS
                                        | PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE);
            }
            // Notify for all SIMs that named this component as their SIM call manager in carrier
            // config, since there may be more than one impacted SIM here.
            simHandlesToNotify = getSimPhoneAccountsFromSimCallManager(account.getAccountHandle());
        } else if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
            // When new SIMs get registered, we notify them of their current voice status override.
            // If there is no SIM call manager for this SIM, we treat that as hasService = false and
            // still notify to ensure consistency.
            if (!registered) {
                // We don't do anything when SIMs are unregistered because we won't have an active
                // subId to map back to phoneId and tell telephony about; that case is handled by
                // telephony internally.
                return;
            }
            PhoneAccountHandle simCallManagerHandle =
                    getSimCallManagerFromHandle(
                            account.getAccountHandle(), account.getAccountHandle().getUserHandle());
            if (simCallManagerHandle != null) {
                PhoneAccount simCallManager = getPhoneAccountUnchecked(simCallManagerHandle);
                hasService =
                        simCallManager != null
                                && simCallManager.hasCapabilities(
                                        PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS
                                                | PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE);
            }
            simHandlesToNotify = Collections.singletonList(account.getAccountHandle());
        } else {
            // Not a relevant account - we only care about CONNECTION_MANAGER and SIM_SUBSCRIPTION.
            return;
        }
        if (simHandlesToNotify.isEmpty()) return;
        Log.i(
                this,
                "Notifying telephony of voice service override change for %d SIMs, hasService = %b",
                simHandlesToNotify.size(),
                hasService);
        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
        for (PhoneAccountHandle simHandle : simHandlesToNotify) {
            // This may be null if there are no active SIMs but the device is still camped for
            // emergency calls and registered a SIM_SUBSCRIPTION for that purpose.
            TelephonyManager simTm = tm.createForPhoneAccountHandle(simHandle);
            if (simTm == null) continue;
            simTm.setVoiceServiceStateOverride(hasService);
        }
    }

    /**
    /**
     * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the
     * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the
     * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission.
     * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission.
+50 −0
Original line number Original line Diff line number Diff line
@@ -532,6 +532,15 @@ public class TelecomServiceImpl {
                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
                            enforceRegisterMultiUser();
                            enforceRegisterMultiUser();
                        }
                        }
                        // These capabilities are for SIM-based accounts only, so only the platform
                        // and carrier-designated SIM call manager can register accounts with these
                        // capabilities.
                        if (account.hasCapabilities(
                                        PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS)
                                || account.hasCapabilities(
                                        PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE)) {
                            enforceRegisterVoiceCallingIndicationCapabilities(account);
                        }
                        Bundle extras = account.getExtras();
                        Bundle extras = account.getExtras();
                        if (extras != null
                        if (extras != null
                                && extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING)) {
                                && extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING)) {
@@ -2343,6 +2352,24 @@ public class TelecomServiceImpl {
        }
        }
    }
    }


    private void enforceRegisterVoiceCallingIndicationCapabilities(PhoneAccount account) {
        // Caller must be able to register a SIM PhoneAccount or be the SIM call manager (as named
        // in carrier config) to declare the two voice indication capabilities.
        boolean prerequisiteCapabilitiesOk =
                account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
                        || account.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER);
        boolean permissionsOk =
                isCallerSimCallManagerForAnySim(account.getAccountHandle())
                        || mContext.checkCallingOrSelfPermission(REGISTER_SIM_SUBSCRIPTION)
                                == PackageManager.PERMISSION_GRANTED;
        if (!prerequisiteCapabilitiesOk || !permissionsOk) {
            throw new SecurityException(
                    "Only SIM subscriptions and connection managers are allowed to declare "
                            + "CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS and "
                            + "CAPABILITY_VOICE_CALLING_AVAILABLE");
        }
    }

    private void enforceRegisterSkipCallFiltering() {
    private void enforceRegisterSkipCallFiltering() {
        if (!isCallerSystemApp()) {
        if (!isCallerSystemApp()) {
            throw new SecurityException(
            throw new SecurityException(
@@ -2553,6 +2580,29 @@ public class TelecomServiceImpl {
        return false;
        return false;
    }
    }


    /**
     * Similar to {@link #isCallerSimCallManager}, but works for all SIMs and does not require
     * {@code accountHandle} to be registered yet.
     */
    private boolean isCallerSimCallManagerForAnySim(PhoneAccountHandle accountHandle) {
        if (isCallerSimCallManager(accountHandle)) {
            // The caller has already registered a CONNECTION_MANAGER PhoneAccount, so let them pass
            // (this allows the SIM call manager through in case of SIM switches, where carrier
            // config may be in a transient state)
            return true;
        }
        // If the caller isn't already registered, then we have to look at the active PSTN
        // PhoneAccounts and check their carrier configs to see if any point to this one's component
        final long token = Binder.clearCallingIdentity();
        try {
            return !mPhoneAccountRegistrar
                    .getSimPhoneAccountsFromSimCallManager(accountHandle)
                    .isEmpty();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private boolean isPrivilegedDialerCalling(String callingPackage) {
    private boolean isPrivilegedDialerCalling(String callingPackage) {
        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);


+4 −0
Original line number Original line Diff line number Diff line
@@ -739,6 +739,10 @@ public class ComponentContextFixture implements TestFixture<Context> {
        return mTelephonyManager;
        return mTelephonyManager;
    }
    }


    public CarrierConfigManager getCarrierConfigManager() {
        return mCarrierConfigManager;
    }

    public NotificationManager getNotificationManager() {
    public NotificationManager getNotificationManager() {
        return mNotificationManager;
        return mNotificationManager;
    }
    }
+213 −0
Original line number Original line Diff line number Diff line
@@ -22,10 +22,16 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
@@ -36,6 +42,7 @@ import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
@@ -43,6 +50,7 @@ import android.telecom.Log;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Xml;
import android.util.Xml;
@@ -1066,6 +1074,156 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
        assertEquals(1, deletedAccounts);
        assertEquals(1, deletedAccounts);
    }
    }


    @Test
    public void testGetSimPhoneAccountsFromSimCallManager() throws Exception {
        // Register the SIM PhoneAccounts
        mComponentContextFixture.addConnectionService(
                makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class));
        PhoneAccount sim1Account = makeQuickSimAccount(1);
        PhoneAccountHandle sim1Handle = sim1Account.getAccountHandle();
        registerAndEnableAccount(sim1Account);
        PhoneAccount sim2Account = makeQuickSimAccount(2);
        PhoneAccountHandle sim2Handle = sim2Account.getAccountHandle();
        registerAndEnableAccount(sim2Account);

        assertEquals(
            List.of(sim1Handle, sim2Handle), mRegistrar.getSimPhoneAccountsOfCurrentUser());

        // Set up the SIM call manager app + carrier configs
        ComponentName simCallManagerComponent =
                new ComponentName("com.carrier.app", "CarrierConnectionService");
        PhoneAccountHandle simCallManagerHandle =
                makeQuickAccountHandle(simCallManagerComponent, "sim-call-manager");
        setSimCallManagerCarrierConfig(
                1, new ComponentName("com.other.carrier", "OtherConnectionService"));
        setSimCallManagerCarrierConfig(2, simCallManagerComponent);

        // Since SIM 1 names another app, so we only get the handle for SIM 2
        assertEquals(
                List.of(sim2Handle),
                mRegistrar.getSimPhoneAccountsFromSimCallManager(simCallManagerHandle));
        // We do exact component matching, not just package name matching
        assertEquals(
                List.of(),
                mRegistrar.getSimPhoneAccountsFromSimCallManager(
                        makeQuickAccountHandle(
                                new ComponentName("com.carrier.app", "SomeOtherUnrelatedService"),
                                "same-pkg-but-diff-svc")));

        // Results are identical after we register the PhoneAccount
        mComponentContextFixture.addConnectionService(
                simCallManagerComponent, Mockito.mock(IConnectionService.class));
        PhoneAccount simCallManagerAccount =
                new PhoneAccount.Builder(simCallManagerHandle, "SIM call manager")
                        .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
                        .build();
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        assertEquals(
                List.of(sim2Handle),
                mRegistrar.getSimPhoneAccountsFromSimCallManager(simCallManagerHandle));
    }

    @Test
    public void testMaybeNotifyTelephonyForVoiceServiceState() throws Exception {
        // Register the SIM PhoneAccounts
        mComponentContextFixture.addConnectionService(
                makeQuickConnectionServiceComponentName(), Mockito.mock(IConnectionService.class));
        PhoneAccount sim1Account = makeQuickSimAccount(1);
        registerAndEnableAccount(sim1Account);
        PhoneAccount sim2Account = makeQuickSimAccount(2);
        registerAndEnableAccount(sim2Account);
        // Telephony is notified by default when new SIM accounts are registered
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Set up the SIM call manager app + carrier configs
        ComponentName simCallManagerComponent =
                new ComponentName("com.carrier.app", "CarrierConnectionService");
        PhoneAccountHandle simCallManagerHandle =
                makeQuickAccountHandle(simCallManagerComponent, "sim-call-manager");
        mComponentContextFixture.addConnectionService(
                simCallManagerComponent, Mockito.mock(IConnectionService.class));
        setSimCallManagerCarrierConfig(1, simCallManagerComponent);
        setSimCallManagerCarrierConfig(2, simCallManagerComponent);

        // When the SIM call manager is registered without the SUPPORTS capability, telephony is
        // still notified for consistency (e.g. runtime capability removal + re-registration).
        PhoneAccount simCallManagerAccount =
                new PhoneAccount.Builder(simCallManagerHandle, "SIM call manager")
                        .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
                        .build();
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Adding the SUPPORTS capability causes the SIMs to get notified with false again for
        // consistency purposes
        simCallManagerAccount =
                copyPhoneAccountAndAddCapabilities(
                        simCallManagerAccount,
                        PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS);
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Adding the AVAILABLE capability updates the SIMs again, this time with hasService = true
        simCallManagerAccount =
                copyPhoneAccountAndAddCapabilities(
                        simCallManagerAccount, PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE);
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(true);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Removing a SIM account does nothing, regardless of SIM call manager capabilities
        mRegistrar.unregisterPhoneAccount(sim1Account.getAccountHandle());
        verify(mComponentContextFixture.getTelephonyManager(), never())
                .setVoiceServiceStateOverride(anyBoolean());
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Adding a SIM account while a SIM call manager with both capabilities is registered causes
        // a call to telephony with hasService = true
        mRegistrar.registerPhoneAccount(sim1Account);
        verify(mComponentContextFixture.getTelephonyManager(), times(1))
                .setVoiceServiceStateOverride(true);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Removing the SIM call manager while it has both capabilities causes a call to telephony
        // with hasService = false
        mRegistrar.unregisterPhoneAccount(simCallManagerHandle);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Removing the SIM call manager while it has the SUPPORTS capability but not AVAILABLE
        // still causes a call to telephony with hasService = false for consistency
        simCallManagerAccount =
                copyPhoneAccountAndRemoveCapabilities(
                        simCallManagerAccount, PhoneAccount.CAPABILITY_VOICE_CALLING_AVAILABLE);
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        clearInvocations(mComponentContextFixture.getTelephonyManager()); // from re-registration
        mRegistrar.unregisterPhoneAccount(simCallManagerHandle);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());

        // Finally, removing the SIM call manager while it has neither capability still causes a
        // call to telephony with hasService = false for consistency
        simCallManagerAccount =
                copyPhoneAccountAndRemoveCapabilities(
                        simCallManagerAccount,
                        PhoneAccount.CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS);
        mRegistrar.registerPhoneAccount(simCallManagerAccount);
        clearInvocations(mComponentContextFixture.getTelephonyManager()); // from re-registration
        mRegistrar.unregisterPhoneAccount(simCallManagerHandle);
        verify(mComponentContextFixture.getTelephonyManager(), times(2))
                .setVoiceServiceStateOverride(false);
        clearInvocations(mComponentContextFixture.getTelephonyManager());
    }

    private static ComponentName makeQuickConnectionServiceComponentName() {
    private static ComponentName makeQuickConnectionServiceComponentName() {
        return new ComponentName(
        return new ComponentName(
                "com.android.server.telecom.tests",
                "com.android.server.telecom.tests",
@@ -1086,6 +1244,23 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
                "label" + idx);
                "label" + idx);
    }
    }


    private static PhoneAccount copyPhoneAccountAndOverrideCapabilities(
            PhoneAccount base, int newCapabilities) {
        return base.toBuilder().setCapabilities(newCapabilities).build();
    }

    private static PhoneAccount copyPhoneAccountAndAddCapabilities(
            PhoneAccount base, int capabilitiesToAdd) {
        return copyPhoneAccountAndOverrideCapabilities(
                base, base.getCapabilities() | capabilitiesToAdd);
    }

    private static PhoneAccount copyPhoneAccountAndRemoveCapabilities(
            PhoneAccount base, int capabilitiesToRemove) {
        return copyPhoneAccountAndOverrideCapabilities(
                base, base.getCapabilities() & ~capabilitiesToRemove);
    }

    private PhoneAccount makeQuickAccount(String id, int idx) {
    private PhoneAccount makeQuickAccount(String id, int idx) {
        return makeQuickAccountBuilder(id, idx)
        return makeQuickAccountBuilder(id, idx)
                .setAddress(Uri.parse("http://foo.com/" + idx))
                .setAddress(Uri.parse("http://foo.com/" + idx))
@@ -1098,6 +1273,44 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
                .build();
                .build();
    }
    }


    /**
     * Similar to {@link #makeQuickAccount}, but also hooks up {@code TelephonyManager} so that it
     * returns {@code simId} as the account's subscriptionId.
     */
    private PhoneAccount makeQuickSimAccount(int simId) {
        PhoneAccount simAccount =
                makeQuickAccountBuilder("sim" + simId, simId)
                        .setCapabilities(
                                PhoneAccount.CAPABILITY_CALL_PROVIDER
                                        | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
                        .setIsEnabled(true)
                        .build();
        when(mComponentContextFixture
                        .getTelephonyManager()
                        .getSubscriptionId(simAccount.getAccountHandle()))
                .thenReturn(simId);
        // mComponentContextFixture already sets up the createForSubscriptionId self-reference
        when(mComponentContextFixture
                        .getTelephonyManager()
                        .createForPhoneAccountHandle(simAccount.getAccountHandle()))
                .thenReturn(mComponentContextFixture.getTelephonyManager());
        return simAccount;
    }

    /**
     * Hooks up carrier config to point to {@code simCallManagerComponent} for the given {@code
     * subscriptionId}.
     */
    private void setSimCallManagerCarrierConfig(
            int subscriptionId, @Nullable ComponentName simCallManagerComponent) {
        PersistableBundle config = new PersistableBundle();
        config.putString(
                CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING,
                simCallManagerComponent != null ? simCallManagerComponent.flattenToString() : null);
        when(mComponentContextFixture.getCarrierConfigManager().getConfigForSubId(subscriptionId))
                .thenReturn(config);
    }

    private static void roundTripPhoneAccount(PhoneAccount original) throws Exception {
    private static void roundTripPhoneAccount(PhoneAccount original) throws Exception {
        PhoneAccount copy = null;
        PhoneAccount copy = null;