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

Commit 413d24de authored by Sahin Caliskan's avatar Sahin Caliskan
Browse files

Implement RcsThread querying (opt)

This change does a first pass to introduce RcsThread querying. We can
now insert threads and query them back.

Test: Added unit test

Bug: 109759350
Change-Id: I24abc76175dba8de1452c698c9e9f83b04e8fce8
parent cbe0de99
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -14,31 +14,44 @@
 * limitations under the License.
 */

package com.android.internal.telephony;
package com.android.internal.telephony.ims;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.ServiceManager;
import android.telephony.Rlog;
import android.telephony.ims.Rcs1To1Thread;
import android.telephony.ims.RcsMessageStore;
import android.telephony.ims.RcsParticipant;
import android.telephony.ims.RcsThread;
import android.telephony.ims.RcsThreadQueryContinuationToken;
import android.telephony.ims.RcsThreadQueryParameters;
import android.telephony.ims.RcsThreadQueryResult;
import android.telephony.ims.aidl.IRcs;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.List;


/** Backing implementation of {@link RcsMessageStore}. */
public class RcsMessageStoreController extends IRcs.Stub {
    public static final String PARTICIPANT_ADDRESS_KEY = "participant_address";
    private static final String TAG = "RcsMessageStoreController";
    private static final String RCS_SERVICE_NAME = "ircs";

    private static RcsMessageStoreController sInstance;

    private final Context mContext;
    private final ContentResolver mContentResolver;

    /** Initialize the instance. Should only be called once. */
    public static RcsMessageStoreController init(Context context) {
        synchronized (RcsMessageStoreController.class) {
            if (sInstance == null) {
                sInstance = new RcsMessageStoreController(context);
                sInstance = new RcsMessageStoreController(context.getContentResolver());
            } else {
                Rlog.e(TAG, "init() called multiple times! sInstance = " + sInstance);
            }
@@ -46,16 +59,16 @@ public class RcsMessageStoreController extends IRcs.Stub {
        return sInstance;
    }

    private RcsMessageStoreController(Context context) {
        mContext = context;
    private RcsMessageStoreController(ContentResolver contentResolver) {
        mContentResolver = contentResolver;
        if (ServiceManager.getService(RCS_SERVICE_NAME) == null) {
            ServiceManager.addService(RCS_SERVICE_NAME, this);
        }
    }

    @VisibleForTesting
    public RcsMessageStoreController(Context context, Void unused) {
        mContext = context;
    public RcsMessageStoreController(ContentResolver contentResolver, Void unused) {
        mContentResolver = contentResolver;
    }

    @Override
@@ -68,4 +81,54 @@ public class RcsMessageStoreController extends IRcs.Stub {
        // TODO - add implementation. Return a magic number for now to test the RPC calls
        return 1018;
    }

    @Override
    public RcsThreadQueryResult getRcsThreads(RcsThreadQueryParameters queryParameters) {
        // TODO - refine implementation to include tokens for the next query
        Cursor rcsThreadsCursor = mContentResolver.query(
                RcsThreadQueryHelper.THREADS_URI,
                RcsThreadQueryHelper.THREAD_PROJECTION,
                RcsThreadQueryHelper.buildWhereClause(queryParameters),
                null,
                queryParameters.isAscending()
                        ? RcsThreadQueryHelper.ASCENDING : RcsThreadQueryHelper.DESCENDING);

        List<RcsThread> rcsThreadList = new ArrayList<>();

        // TODO - currently this only creates 1 to 1 threads - fix this
        while (rcsThreadsCursor != null && rcsThreadsCursor.moveToNext()) {
            Rcs1To1Thread rcs1To1Thread = new Rcs1To1Thread(rcsThreadsCursor.getInt(0));
            rcsThreadList.add(rcs1To1Thread);
        }

        return new RcsThreadQueryResult(null, rcsThreadList);
    }

    @Override
    public RcsThreadQueryResult getRcsThreadsWithToken(
            RcsThreadQueryContinuationToken continuationToken) {
        // TODO - implement
        return null;
    }

    @Override
    public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
        // TODO - use recipient to add a thread

        ContentValues contentValues = new ContentValues(0);
        mContentResolver.insert(RcsThreadQueryHelper.THREADS_URI, contentValues);

        return null;
    }

    @Override
    public RcsParticipant createRcsParticipant(String canonicalAddress) {
        // TODO - refine implementation to disallow duplicate participants
        ContentValues contentValues = new ContentValues();
        contentValues.put(PARTICIPANT_ADDRESS_KEY, canonicalAddress);
        mContentResolver.insert(RcsThreadQueryHelper.PARTICIPANTS_URI, contentValues);

        // TODO - return the newly created participant
        return null;
    }
}
+38 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -13,31 +13,26 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.internal.telephony.ims;

package com.android.internal.telephony.rcs;

import static org.junit.Assert.assertEquals;

import com.android.internal.telephony.RcsMessageStoreController;
import com.android.internal.telephony.TelephonyTest;

import org.junit.Before;
import org.junit.Test;

public class RcsMessageStoreControllerTest extends TelephonyTest {

    private RcsMessageStoreController mRcsMessageStoreController;

    @Before
    public void setUp() {
        mRcsMessageStoreController = new RcsMessageStoreController(mContext, null);
    }
import android.net.Uri;
import android.telephony.ims.RcsThreadQueryParameters;

/**
     * TODO(sahinc): fix the test once there is an implementation in place
 * A helper class focused on querying RCS threads from the
 * {@link com.android.providers.telephony.RcsProvider}
 */
    @Test
    public void testGetMessageCount() {
        assertEquals(1018, mRcsMessageStoreController.getMessageCount(0));
class RcsThreadQueryHelper {
    static final String ASCENDING = "ASCENDING";
    static final String DESCENDING = "DESCENDING";
    static final String THREAD_ID = "_id";

    static final Uri THREADS_URI = Uri.parse("content://rcs/thread");
    static final Uri PARTICIPANTS_URI = Uri.parse("content://rcs/participant");
    static final String[] THREAD_PROJECTION = new String[]{THREAD_ID};

    static String buildWhereClause(RcsThreadQueryParameters queryParameters) {
        // TODO - implement
        return null;
    }
}
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.ims;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doReturn;

import android.database.Cursor;
import android.net.Uri;
import android.telephony.ims.RcsParticipant;
import android.telephony.ims.RcsThreadQueryParameters;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;

import com.android.internal.telephony.TelephonyTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class RcsMessageStoreControllerTest extends TelephonyTest {

    private RcsMessageStoreController mRcsMessageStoreController;
    private MockContentResolver mContentResolver;
    private FakeRcsProvider mFakeRcsProvider;

    @Mock
    RcsParticipant mMockParticipant;

    @Before
    public void setUp() throws Exception {
        super.setUp("RcsMessageStoreControllerTest");
        MockitoAnnotations.initMocks(this);

        mFakeRcsProvider = new FakeRcsProvider();
        mContentResolver = (MockContentResolver) mContext.getContentResolver();
        mContentResolver.addProvider("rcs", mFakeRcsProvider);

        mRcsMessageStoreController = new RcsMessageStoreController(mContentResolver, null);
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @Test
    public void testGetRcsThreads() {
        doReturn(123).when(mMockParticipant).getId();
        RcsThreadQueryParameters queryParameters =
                RcsThreadQueryParameters.builder().withParticipant(mMockParticipant).isGroupThread(
                        true).limitResultsTo(30).sort(true).build();

        mFakeRcsProvider.setExpectedQueryParameters(Uri.parse("content://rcs/thread"),
                new String[]{"_id"}, null, null, "ASCENDING");
        mRcsMessageStoreController.getRcsThreads(queryParameters);
    }

    /**
     * TODO(sahinc): fix the test once there is an implementation in place
     */
    @Test
    public void testGetMessageCount() {
        assertEquals(1018, mRcsMessageStoreController.getMessageCount(0));
    }

    /**
     * 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 FakeRcsProvider extends MockContentProvider {
        private Uri mExpectedUri;
        private String[] mExpectedProjection;
        private String mExpectedWhereClause;
        private String[] mExpectedWhereArgs;
        private String mExpectedSortOrder;

        void setExpectedQueryParameters(Uri uri, String[] projection, String whereClause,
                String[] whereArgs, String sortOrder) {
            mExpectedUri = uri;
            mExpectedProjection = projection;
            mExpectedWhereClause = whereClause;
            mExpectedWhereArgs = whereArgs;
            mExpectedSortOrder = sortOrder;
        }

        @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;
        }

    }
}