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

Commit d69b23be authored by Leland Miller's avatar Leland Miller
Browse files

Remove dependency on canonical-addresses

Currently, canonical-addresses is our only dependency on MmsProvider.
Currently our access requirements to the table is also slightly
different than current usage. To avoid modifying permissions to allow
RcsMessageStoreController to reliably access MmsProvider, and to provide
access in the cleanest manner for the RcsMessageStoreController, this
will provide a proxy to the canonical-addresses table specifically for
RCS message store purposes.

Note that the way RCS message storage interacts with canonical-addresses
may change in the future as well.

Test: Tested manually against future CtsRcsTestCases package
Change-Id: I06662d96a8b2979cd11ff05001953c95968278df
parent a9b5a3eb
Loading
Loading
Loading
Loading
+14 −38
Original line number Diff line number Diff line
@@ -87,7 +87,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.BaseColumns;
import android.provider.Telephony;
import android.telephony.Rlog;
import android.telephony.ims.RcsEventQueryParams;
@@ -302,51 +301,28 @@ public class RcsMessageStoreController extends IRcs.Stub {
     */
    @Override
    public int createRcsParticipant(String canonicalAddress, String alias) throws RemoteException {
        // Lookup the participant in RcsProvider to get the canonical row id in MmsSmsProvider
        int rowInCanonicalAddressesTable = Integer.MIN_VALUE;
        try (Cursor cursor = mContentResolver.query(
                RcsParticipantQueryHelper.CANONICAL_ADDRESSES_URI,
                new String[]{BaseColumns._ID}, Telephony.CanonicalAddressesColumns.ADDRESS + "=?",
                new String[]{canonicalAddress}, null)) {
            if (cursor != null && cursor.getCount() == 1 && cursor.moveToNext()) {
                rowInCanonicalAddressesTable = cursor.getInt(0);
            }
        }

        ContentValues contentValues = new ContentValues();
        contentValues.put(Telephony.CanonicalAddressesColumns.ADDRESS, canonicalAddress);

        if (rowInCanonicalAddressesTable == Integer.MIN_VALUE) {
            // We couldn't find any existing canonical addresses. Add a new one.
            Uri newCanonicalAddress = mContentResolver.insert(
                    RcsParticipantQueryHelper.CANONICAL_ADDRESSES_URI, contentValues);
            if (newCanonicalAddress != null) {
                try {
                    rowInCanonicalAddressesTable = Integer.parseInt(
                            newCanonicalAddress.getLastPathSegment());
                } catch (NumberFormatException e) {
                    throw new RemoteException(
                            "Uri returned after canonical address insertion is malformed: "
                                    + newCanonicalAddress);
                }
            }
        long canonicalAddressId = Telephony.RcsColumns.RcsCanonicalAddressHelper
                .getOrCreateCanonicalAddressId(mContentResolver, canonicalAddress);

        if (canonicalAddressId == TRANSACTION_FAILED) {
            throw new RemoteException("Could not create or make canonical address entry");
        }

        // Now we have a row in canonical_addresses table, and its value is in
        // rowInCanonicalAddressesTable. Put this row id in RCS participants table.
        contentValues.clear();
        contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, rowInCanonicalAddressesTable);
        contentValues.put(CANONICAL_ADDRESS_ID_COLUMN, canonicalAddressId);
        contentValues.put(RCS_ALIAS_COLUMN, alias);

        // TODO (123719857) - Disallow creation of duplicate participants
        Uri newParticipantUri = mContentResolver.insert(RCS_PARTICIPANT_URI, contentValues);
        int newParticipantRowId;

        try {
            if (newParticipantUri != null) {
                newParticipantRowId = Integer.parseInt(newParticipantUri.getLastPathSegment());
            } else {
                // TODO (123719857) - Disallow creation of duplicate participants
        if (newParticipantUri == null) {
            throw new RemoteException("Error inserting new participant into RcsProvider");
        }

        try {
            newParticipantRowId = Integer.parseInt(newParticipantUri.getLastPathSegment());
        } catch (NumberFormatException e) {
            throw new RemoteException(
                    "Uri returned after creating a participant is malformed: " + newParticipantUri);
+84 −44
Original line number Diff line number Diff line
@@ -23,77 +23,117 @@ import android.database.Cursor;
import android.net.Uri;
import android.test.mock.MockContentProvider;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Mocking/spying ContentProviders break in different ways. Use a fake instead. The
 * RcsMessageStoreController doesn't care if RcsProvider works as expected (and doesn't have
 * visibility into it) - so verifying whether we use the correct parameters should suffice.
 */
class FakeProviderWithAsserts extends MockContentProvider {
    private Uri mExpectedUri;
    private String[] mExpectedProjection;
    private String mExpectedWhereClause;
    private String[] mExpectedWhereArgs;
    private String mExpectedSortOrder;
    private ContentValues mExpectedContentValues;

    private Cursor mQueryReturnValue;
    private Uri mInsertReturnValue;

    void setExpectedQueryParameters(Uri uri, String[] projection, String whereClause,
            String[] whereArgs, String sortOrder) {
        mExpectedUri = uri;
        mExpectedProjection = projection;
        mExpectedWhereClause = whereClause;
        mExpectedWhereArgs = whereArgs;
        mExpectedSortOrder = sortOrder;
    }
    /**
     * Interface implemented by all classes that describe expected operations.
     */
    private interface ExpectedOperation {}

    void setExpectedInsertParameters(Uri uri, ContentValues contentValues) {
        mExpectedUri = uri;
        mExpectedContentValues = contentValues;
    static class ExpectedQuery implements ExpectedOperation {
        private final Uri mUri;
        private final String[] mProjection;
        private final String mWhereClause;
        private final String[] mWhereArgs;
        private final String mSortOrder;
        private final Cursor mReturnValue;

        ExpectedQuery(Uri uri, String[] projection, String whereClause, String[] whereArgs,
                String sortOrder, Cursor returnValue) {
            mUri = uri;
            mProjection = projection;
            mWhereClause = whereClause;
            mWhereArgs = whereArgs;
            mSortOrder = sortOrder;
            mReturnValue = returnValue;
        }
    }

    void setExpectedUpdateParameters(Uri uri, ContentValues contentValues, String whereClause,
            String[] whereArgs) {
        mExpectedUri = uri;
        mExpectedContentValues = contentValues;
        mExpectedWhereClause = whereClause;
        mExpectedWhereArgs = whereArgs;
    static class ExpectedInsert implements ExpectedOperation {
        private Uri mUri;
        private ContentValues mContentValues;
        private Uri mReturnValue;

        ExpectedInsert(Uri uri, ContentValues contentValues, Uri returnValue) {
            mUri = uri;
            mContentValues = contentValues;
            mReturnValue = returnValue;
        }
    }

    static class ExpectedUpdate implements ExpectedOperation {
        private Uri mUri;
        private String mWhereClause;
        private String[] mWhereArgs;
        private ContentValues mContentValues;
        private int mReturnValue;

    void setInsertReturnValue(Uri returnValue) {
        mInsertReturnValue = returnValue;
        ExpectedUpdate(Uri uri, String whereClause, String[] whereArgs,
                ContentValues contentValues, int returnValue) {
            mUri = uri;
            mWhereClause = whereClause;
            mWhereArgs = whereArgs;
            mContentValues = contentValues;
            mReturnValue = returnValue;
        }
    }

    private Queue<ExpectedOperation> mExpectedOperations = new LinkedList<>();

    void setQueryReturnValue(Cursor cursor) {
    void addExpectedOperation(ExpectedOperation expectedOperation) {
        mExpectedOperations.add(expectedOperation);
    }

    private <T extends ExpectedOperation> T getExpectedOperation(Class<T> clazz) {
        ExpectedOperation expectedOperation = mExpectedOperations.remove();
        assertThat(expectedOperation).isNotNull();
        assertThat(expectedOperation).isInstanceOf(clazz);
        return clazz.cast(expectedOperation);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String whereClause, String[] whereArgs,
            String sortOrder) {
        assertThat(uri).isEqualTo(mExpectedUri);
        assertThat(projection).isEqualTo(mExpectedProjection);
        assertThat(whereClause).isEqualTo(mExpectedWhereClause);
        assertThat(whereArgs).isEqualTo(mExpectedWhereArgs);
        assertThat(sortOrder).isEqualTo(mExpectedSortOrder);
        return null;
        ExpectedQuery expectedQuery = getExpectedOperation(ExpectedQuery.class);
        assertThat(uri).isEqualTo(expectedQuery.mUri);
        assertThat(projection).isEqualTo(expectedQuery.mProjection);
        assertThat(whereClause).isEqualTo(expectedQuery.mWhereClause);
        assertThat(whereArgs).isEqualTo(expectedQuery.mWhereArgs);
        assertThat(sortOrder).isEqualTo(expectedQuery.mSortOrder);
        return expectedQuery.mReturnValue;
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        assertThat(uri).isEqualTo(mExpectedUri);
        assertThat(contentValues).isEqualTo(mExpectedContentValues);
        return mInsertReturnValue;
        ExpectedInsert expectedInsert = getExpectedOperation(ExpectedInsert.class);
        assertThat(uri).isEqualTo(expectedInsert.mUri);
        assertThatContentValuesAreEquivalent(contentValues, expectedInsert.mContentValues);
        return expectedInsert.mReturnValue;
    }

    @Override
    public int update(Uri uri, ContentValues contentValues, String whereClause,
            String[] whereArgs) {
        assertThat(uri).isEqualTo(mExpectedUri);
        assertThat(contentValues).isEqualTo(mExpectedContentValues);
        assertThat(whereClause).isEqualTo(mExpectedWhereClause);
        assertThat(whereArgs).isEqualTo(mExpectedWhereArgs);
        return 0;
        ExpectedUpdate expectedUpdate = getExpectedOperation(ExpectedUpdate.class);
        assertThat(uri).isEqualTo(expectedUpdate.mUri);
        assertThatContentValuesAreEquivalent(contentValues, expectedUpdate.mContentValues);
        assertThat(whereClause).isEqualTo(expectedUpdate.mWhereClause);
        assertThat(whereArgs).isEqualTo(expectedUpdate.mWhereArgs);
        return expectedUpdate.mReturnValue;
    }

    private void assertThatContentValuesAreEquivalent(
            ContentValues actual, ContentValues expected) {
        assertThat(actual.size()).isEqualTo(expected.size());
        for (String key : expected.keySet()) {
            assertThat(actual.get(key)).isEqualTo(expected.get(key));
        }
    }
}
+40 −32
Original line number Diff line number Diff line
@@ -27,13 +27,19 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;

import android.content.ContentValues;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.Telephony;
import android.provider.Telephony.RcsColumns.RcsParticipantColumns;
import android.telephony.ims.RcsParticipant;
import android.telephony.ims.RcsThreadQueryParams;
import android.test.mock.MockContentResolver;

import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.ims.FakeProviderWithAsserts.ExpectedInsert;
import com.android.internal.telephony.ims.FakeProviderWithAsserts.ExpectedQuery;
import com.android.internal.telephony.ims.FakeProviderWithAsserts.ExpectedUpdate;

import org.junit.After;
import org.junit.Before;
@@ -46,7 +52,6 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    private RcsMessageStoreController mRcsMessageStoreController;
    private MockContentResolver mContentResolver;
    private FakeProviderWithAsserts mFakeRcsProvider;
    private FakeProviderWithAsserts mFakeMmsSmsProvider;

    @Mock
    RcsParticipant mMockParticipant;
@@ -57,10 +62,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
        MockitoAnnotations.initMocks(this);

        mFakeRcsProvider = new FakeProviderWithAsserts();
        mFakeMmsSmsProvider = new FakeProviderWithAsserts();
        mContentResolver = (MockContentResolver) mContext.getContentResolver();
        mContentResolver.addProvider("rcs", mFakeRcsProvider);
        mContentResolver.addProvider("mms-sms", mFakeMmsSmsProvider);

        mRcsMessageStoreController = new RcsMessageStoreController(mContentResolver, null);
    }
@@ -78,8 +81,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
                        .setThreadType(THREAD_TYPE_GROUP).setResultLimit(30).build();

        // TODO - limit the query as per queryParameters. This will change how the query is executed
        mFakeRcsProvider.setExpectedQueryParameters(Uri.parse("content://rcs/thread"), null, null,
                null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedQuery(
                Uri.parse("content://rcs/thread"), null, null, null, null, null));

        try {
            mRcsMessageStoreController.getRcsThreads(queryParameters);
@@ -90,27 +93,32 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {

    @Test
    public void testCreateRcsParticipant() throws RemoteException {
        // verify the first query to existing canonical addresses
        mFakeMmsSmsProvider.setExpectedQueryParameters(
                Uri.parse("content://mms-sms/canonical-addresses"), new String[]{"_id"},
                "address=?", new String[]{"+5551234567"}, null);

        // verify the insert on canonical addresses
        ContentValues expectedMmsSmsValues = new ContentValues(1);
        expectedMmsSmsValues.put("address", "+5551234567");
        mFakeMmsSmsProvider.setInsertReturnValue(
                Uri.parse("content://mms-sms/canonical-address/456"));
        mFakeMmsSmsProvider.setExpectedInsertParameters(
                Uri.parse("content://mms-sms/canonical-addresses"), expectedMmsSmsValues);
        String canonicalAddress = "+5551234567";

        // verify the first query to canonical addresses
        MatrixCursor canonicalAddressQueryCursor = new MatrixCursor(
                new String[]{Telephony.CanonicalAddressesColumns._ID});
        canonicalAddressQueryCursor.addRow(new Object[]{456});

        Uri expectedCanonicalAddressUri = Uri.parse("content://rcs/canonical-address")
                .buildUpon()
                .appendQueryParameter("address", canonicalAddress)
                .build();

        mFakeRcsProvider.addExpectedOperation(new ExpectedQuery(
                expectedCanonicalAddressUri, null, null, null, null, canonicalAddressQueryCursor));


        // verify the final insert on rcs participants
        ContentValues expectedRcsValues = new ContentValues(1);
        expectedRcsValues.put("canonical_address_id", 456);
        mFakeRcsProvider.setInsertReturnValue(Uri.parse("content://rcs/participant/1001"));
        mFakeRcsProvider.setExpectedInsertParameters(Uri.parse("content://rcs/participant"),
                expectedRcsValues);
        expectedRcsValues.put(RcsParticipantColumns.CANONICAL_ADDRESS_ID_COLUMN, 456);
        expectedRcsValues.put(RcsParticipantColumns.RCS_ALIAS_COLUMN, "alias");
        mFakeRcsProvider.addExpectedOperation(new ExpectedInsert(
                Uri.parse("content://rcs/participant"), expectedRcsValues,
                Uri.parse("content://rcs/participant/1001")));

        int participantId = mRcsMessageStoreController.createRcsParticipant("+5551234567", "alias");
        int participantId =
                mRcsMessageStoreController.createRcsParticipant(canonicalAddress, "alias");

        assertThat(participantId).isEqualTo(1001);
    }
@@ -119,8 +127,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    public void testUpdateRcsParticipantAlias() {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put("rcs_alias", "New Alias");
        mFakeRcsProvider.setExpectedUpdateParameters(Uri.parse("content://rcs/participant/551"),
                contentValues, null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedUpdate(
                Uri.parse("content://rcs/participant/551"), null, null, contentValues, 0));

        try {
            mRcsMessageStoreController.setRcsParticipantAlias(551, "New Alias");
@@ -133,8 +141,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    public void testSet1To1ThreadFallbackThreadId() {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(FALLBACK_THREAD_ID_COLUMN, 456L);
        mFakeRcsProvider.setExpectedUpdateParameters(Uri.parse("content://rcs/p2p_thread/123"),
                contentValues, null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedUpdate(
                Uri.parse("content://rcs/p2p_thread/123"), null, null, contentValues, 0));
        try {
            mRcsMessageStoreController.set1To1ThreadFallbackThreadId(123, 456L);
        } catch (RemoteException e) {
@@ -146,8 +154,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    public void testSetGroupThreadName() {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(GROUP_NAME_COLUMN, "new name");
        mFakeRcsProvider.setExpectedUpdateParameters(Uri.parse("content://rcs/group_thread/345"),
                contentValues, null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedUpdate(
                Uri.parse("content://rcs/group_thread/345"), null, null, contentValues, 0));

        try {
            mRcsMessageStoreController.setGroupThreadName(345, "new name");
@@ -160,8 +168,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    public void testSetGroupThreadIcon() {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(GROUP_ICON_COLUMN, "newIcon");
        mFakeRcsProvider.setExpectedUpdateParameters(Uri.parse("content://rcs/group_thread/345"),
                contentValues, null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedUpdate(
                Uri.parse("content://rcs/group_thread/345"), null, null, contentValues, 0));

        try {
            mRcsMessageStoreController.setGroupThreadIcon(345, Uri.parse("newIcon"));
@@ -174,8 +182,8 @@ public class RcsMessageStoreControllerTest extends TelephonyTest {
    public void testSetGroupThreadOwner() {
        ContentValues contentValues = new ContentValues(1);
        contentValues.put(OWNER_PARTICIPANT_COLUMN, 9);
        mFakeRcsProvider.setExpectedUpdateParameters(Uri.parse("content://rcs/group_thread/454"),
                contentValues, null, null);
        mFakeRcsProvider.addExpectedOperation(new ExpectedUpdate(
                Uri.parse("content://rcs/group_thread/454"), null, null, contentValues, 0));

        RcsParticipant participant = new RcsParticipant(9);