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

Commit 32b28cbd authored by Thomas Stuart's avatar Thomas Stuart
Browse files

enforce a character limit in PhoneAccountRegistrar for PAs

prevent large character strings in PhoneAccount and PhoneAccountHandle
fields that may cause issues when writing to device. Such fields will
now throw an IllegalArgumentException when the character count is over
the set value.

Test: 4 unit tests
bug: 256819769
Change-Id: Ib54b625c942969873618557e174b0f825e5a5a84
parent f3d6c6d3
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -154,6 +154,8 @@ public class PhoneAccountRegistrar {
    @VisibleForTesting
    public static final int EXPECTED_STATE_VERSION = 9;
    public static final int MAX_PHONE_ACCOUNT_REGISTRATIONS = 10;
    public static final int MAX_PHONE_ACCOUNT_EXTAS_KEY_PAIR_LIMIT = 100;
    public static final int MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT = 256;

    /** Keep in sync with the same in SipSettings.java */
    private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
@@ -874,10 +876,63 @@ public class PhoneAccountRegistrar {
                            + " because the limit, " + MAX_PHONE_ACCOUNT_REGISTRATIONS
                            + ", has been reached");
        }
        // Enforce a character limit on all PA and PAH string or char-sequence fields.
        enforceCharacterLimit(account);

        addOrReplacePhoneAccount(account);
    }

    /**
     * All {@link PhoneAccount} and{@link PhoneAccountHandle} String and Char-Sequence fields
     * should be restricted to character limit of MAX_PHONE_ACCOUNT_CHAR_LIMIT to prevent exceptions
     * when writing large character streams to XML-Serializer.
     *
     * @param account to enforce character limit checks on
     */
    public void enforceCharacterLimit(PhoneAccount account) {
        if (account == null) {
            return;
        }
        PhoneAccountHandle handle = account.getAccountHandle();

        String[] fields =
                {"Package Name", "Class Name", "PhoneAccountHandle Id", "Label", "ShortDescription",
                        "GroupId"};
        CharSequence[] args = {handle.getComponentName().getPackageName(),
                handle.getComponentName().getClassName(), handle.getId(), account.getLabel(),
                account.getShortDescription(), account.getGroupId()};

        for (int i = 0; i < fields.length; i++) {
            if (args[i] != null && args[i].length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) {
                throw new IllegalArgumentException("The PhoneAccount or PhoneAccountHandle"
                        + fields[i] + " field has an invalid character count. PhoneAccount and "
                        + "PhoneAccountHandle String and Char-Sequence fields are limited to "
                        + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " characters.");
            }
        }

        Bundle extras = account.getExtras();
        if (extras != null) {
            if (extras.keySet().size() > MAX_PHONE_ACCOUNT_EXTAS_KEY_PAIR_LIMIT) {
                throw new IllegalArgumentException("The PhoneAccount#mExtras is limited to " +
                        MAX_PHONE_ACCOUNT_EXTAS_KEY_PAIR_LIMIT + " (key,value) pairs.");
            }

            for (String key : extras.keySet()) {
                Object value = extras.get(key);

                if ((key != null && key.length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) ||
                        (value instanceof String &&
                                ((String) value).length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT)) {
                    throw new IllegalArgumentException("The PhoneAccount#mExtras contains a String"
                            + " key or value that has an invalid character count. PhoneAccount and "
                            + "PhoneAccountHandle String and Char-Sequence fields are limited to "
                            + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " characters.");
                }
            }
        }
    }

    /**
     * Adds a {@code PhoneAccount}, replacing an existing one if found.
     *
+252 −0
Original line number Diff line number Diff line
@@ -89,13 +89,18 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@RunWith(JUnit4.class)
public class PhoneAccountRegistrarTest extends TelecomTestCase {

    private static final int MAX_VERSION = Integer.MAX_VALUE;
    private static final int INVALID_CHAR_LIMIT_COUNT =
            PhoneAccountRegistrar.MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + 1;
    private static final String INVALID_STR = "a".repeat(INVALID_CHAR_LIMIT_COUNT);
    private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
    private static final String TEST_LABEL = "right";
    private static final String TEST_ID = "123";
    private final String PACKAGE_1 = "PACKAGE_1";
    private final String PACKAGE_2 = "PACKAGE_2";
    private final String COMPONENT_NAME = "com.android.server.telecom.tests.MockConnectionService";
@@ -1334,6 +1339,253 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
                defaultPhoneAccountHandle.phoneAccountHandle.getId());
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccountHandle} with a { PhoneAccountHandle#packageName} that is over the
     * character limit set
     */
    @Test
    public void testInvalidPhoneAccountHandlePackageNameThrowsException() {
        // GIVEN
        String invalidPackageName = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(
                new ComponentName(invalidPackageName, this.getClass().getName()), TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidPackageName,
                    account.getAccountHandle().getComponentName().getPackageName());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccountHandle} with a { PhoneAccountHandle#className} that is over the
     * character limit set
     */
    @Test
    public void testInvalidPhoneAccountHandleClassNameThrowsException() {
        // GIVEN
        String invalidClassName = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(
                new ComponentName(this.getClass().getPackageName(), invalidClassName), TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidClassName,
                    account.getAccountHandle().getComponentName().getClassName());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccountHandle} with a { PhoneAccount#mId} that is over the character limit set
     */
    @Test
    public void testInvalidPhoneAccountHandleIdThrowsException() {
        // GIVEN
        String invalidId = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(invalidId);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidId, account.getAccountHandle().getId());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a { PhoneAccount#mLabel} that is over the character limit set
     */
    @Test
    public void testInvalidLabelThrowsException() {
        // GIVEN
        String invalidLabel = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, invalidLabel)
                .setCapabilities(PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS);

        // WHEN
        when(mAppLabelProxy.getAppLabel(anyString())).thenReturn(invalidLabel);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidLabel, account.getLabel());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a {PhoneAccount#mShortDescription} that is over the character
     * limit set
     */
    @Test
    public void testInvalidShortDescriptionThrowsException() {
        // GIVEN
        String invalidShortDescription = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle)
                .setShortDescription(invalidShortDescription);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidShortDescription, account.getShortDescription());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a {PhoneAccount#mGroupId} that is over the character limit set
     */
    @Test
    public void testInvalidGroupIdThrowsException() {
        // GIVEN
        String invalidGroupId = INVALID_STR;
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle)
                .setGroupId(invalidGroupId);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidGroupId, account.getGroupId());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a {PhoneAccount#mExtras} that is over the character limit set
     */
    @Test
    public void testInvalidExtraStringKeyThrowsException() {
        // GIVEN
        String invalidBundleKey = INVALID_STR;
        String keyValue = "value";
        Bundle extras = new Bundle();
        extras.putString(invalidBundleKey, keyValue);
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle)
                .setExtras(extras);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(keyValue, account.getExtras().getString(invalidBundleKey));
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a {PhoneAccount#mExtras} that is over the character limit set
     */
    @Test
    public void testInvalidExtraStringValueThrowsException() {
        // GIVEN
        String extrasKey = "ExtrasStringKey";
        String invalidBundleValue = INVALID_STR;
        Bundle extras = new Bundle();
        extras.putString(extrasKey, invalidBundleValue);
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle)
                .setExtras(extras);

        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidBundleValue, account.getExtras().getString(extrasKey));
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // pass test
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    /**
     * Test that an {@link IllegalArgumentException} is thrown when a package registers a
     * {@link PhoneAccount} with a {PhoneAccount#mExtras} that is over the (key,value) pair limit
     */
    @Test
    public void testInvalidExtraElementsExceedsLimitAndThrowsException() {
        // GIVEN
        int invalidBundleExtrasLimit =
                PhoneAccountRegistrar.MAX_PHONE_ACCOUNT_EXTAS_KEY_PAIR_LIMIT + 1;
        Bundle extras = new Bundle();
        for (int i = 0; i < invalidBundleExtrasLimit; i++) {
            extras.putString(UUID.randomUUID().toString(), "value");
        }
        PhoneAccountHandle handle = makeQuickAccountHandle(TEST_ID);
        PhoneAccount.Builder builder = makeBuilderWithBindCapabilities(handle)
                .setExtras(extras);
        // THEN
        try {
            PhoneAccount account = builder.build();
            assertEquals(invalidBundleExtrasLimit, account.getExtras().size());
            mRegistrar.registerPhoneAccount(account);
            fail("failed to throw IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // Test Pass
        } finally {
            mRegistrar.unregisterPhoneAccount(handle);
        }
    }

    private static PhoneAccount.Builder makeBuilderWithBindCapabilities(PhoneAccountHandle handle){
        return new PhoneAccount.Builder(handle, TEST_LABEL)
                .setCapabilities(PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS);
    }

    private static ComponentName makeQuickConnectionServiceComponentName() {
        return new ComponentName(
                "com.android.server.telecom.tests",