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

Commit 18aafd8c authored by Hyundo Moon's avatar Hyundo Moon Committed by Gerrit Code Review
Browse files

Merge "Add BluetoothPbapUtilsTest"

parents 796b1637 d7d41975
Loading
Loading
Loading
Loading
+42 −24
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContactsEntity;
import android.util.Log;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.vcard.VCardComposer;
import com.android.vcard.VCardConfig;

@@ -68,13 +70,17 @@ class BluetoothPbapUtils {

    static long sPrimaryVersionCounter = 0;
    static long sSecondaryVersionCounter = 0;
    private static long sTotalContacts = 0;
    @VisibleForTesting
    static long sTotalContacts = 0;

    /* totalFields and totalSvcFields used to update primary/secondary version
     * counter between pbap sessions*/
    private static long sTotalFields = 0;
    private static long sTotalSvcFields = 0;
    private static long sContactsLastUpdated = 0;
    @VisibleForTesting
    static long sTotalFields = 0;
    @VisibleForTesting
    static long sTotalSvcFields = 0;
    @VisibleForTesting
    static long sContactsLastUpdated = 0;

    private static class ContactData {
        private String mName;
@@ -97,14 +103,20 @@ class BluetoothPbapUtils {
        }
    }

    private static HashMap<String, ContactData> sContactDataset = new HashMap<>();
    @VisibleForTesting
    static HashMap<String, ContactData> sContactDataset = new HashMap<>();

    private static HashSet<String> sContactSet = new HashSet<>();
    @VisibleForTesting
    static HashSet<String> sContactSet = new HashSet<>();

    private static final String TYPE_NAME = "name";
    private static final String TYPE_PHONE = "phone";
    private static final String TYPE_EMAIL = "email";
    private static final String TYPE_ADDRESS = "address";
    @VisibleForTesting
    static final String TYPE_NAME = "name";
    @VisibleForTesting
    static final String TYPE_PHONE = "phone";
    @VisibleForTesting
    static final String TYPE_EMAIL = "email";
    @VisibleForTesting
    static final String TYPE_ADDRESS = "address";

    private static boolean hasFilter(byte[] filter) {
        return filter != null && filter.length > 0;
@@ -171,8 +183,9 @@ class BluetoothPbapUtils {
    }

    public static String getProfileName(Context context) {
        Cursor c = context.getContentResolver()
                .query(Profile.CONTENT_URI, new String[]{Profile.DISPLAY_NAME}, null, null, null);
        Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(
                context.getContentResolver(), Profile.CONTENT_URI,
                new String[]{Profile.DISPLAY_NAME}, null, null, null);
        String ownerName = null;
        if (c != null && c.moveToFirst()) {
            ownerName = c.getString(0);
@@ -267,8 +280,8 @@ class BluetoothPbapUtils {
        HashSet<String> currentContactSet = new HashSet<>();

        String[] projection = {Contacts._ID, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP};
        Cursor c = context.getContentResolver()
                .query(Contacts.CONTENT_URI, projection, null, null, null);
        Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(
                context.getContentResolver(), Contacts.CONTENT_URI, projection, null, null, null);

        if (c == null) {
            Log.d(TAG, "Failed to fetch data from contact database");
@@ -320,8 +333,9 @@ class BluetoothPbapUtils {
            for (String deletedContact : deletedContacts) {
                sContactSet.remove(deletedContact);
                String[] selectionArgs = {deletedContact};
                Cursor dataCursor = context.getContentResolver()
                        .query(Data.CONTENT_URI, dataProjection, whereClause, selectionArgs, null);
                Cursor dataCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(
                        context.getContentResolver(), Data.CONTENT_URI, dataProjection, whereClause,
                        selectionArgs, null);

                if (dataCursor == null) {
                    Log.d(TAG, "Failed to fetch data from contact database");
@@ -350,8 +364,9 @@ class BluetoothPbapUtils {
                boolean updated = false;

                String[] selectionArgs = {contact};
                Cursor dataCursor = context.getContentResolver()
                        .query(Data.CONTENT_URI, dataProjection, whereClause, selectionArgs, null);
                Cursor dataCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(
                        context.getContentResolver(), Data.CONTENT_URI, dataProjection, whereClause,
                        selectionArgs, null);

                if (dataCursor == null) {
                    Log.d(TAG, "Failed to fetch data from contact database");
@@ -419,7 +434,8 @@ class BluetoothPbapUtils {
    /* checkFieldUpdates checks update contact fields of a particular contact.
     * Field update can be a field updated/added/deleted in an existing contact.
     * Returns true if any contact field is updated else return false. */
    private static boolean checkFieldUpdates(ArrayList<String> oldFields,
    @VisibleForTesting
    static boolean checkFieldUpdates(ArrayList<String> oldFields,
            ArrayList<String> newFields) {
        if (newFields != null && oldFields != null) {
            if (newFields.size() != oldFields.size()) {
@@ -451,11 +467,13 @@ class BluetoothPbapUtils {
    /* fetchAndSetContacts reads contacts and caches them
     * isLoad = true indicates its loading all contacts
     * isLoad = false indiacates its caching recently added contact in database*/
    private static int fetchAndSetContacts(Context context, Handler handler, String[] projection,
    @VisibleForTesting
    static int fetchAndSetContacts(Context context, Handler handler, String[] projection,
            String whereClause, String[] selectionArgs, boolean isLoad) {
        long currentTotalFields = 0, currentSvcFieldCount = 0;
        Cursor c = context.getContentResolver()
                .query(Data.CONTENT_URI, projection, whereClause, selectionArgs, null);
        Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(
                context.getContentResolver(), Data.CONTENT_URI, projection, whereClause,
                selectionArgs, null);

        /* send delayed message to loadContact when ContentResolver is unable
         * to fetch data from contact database using the specified URI at that
@@ -540,8 +558,8 @@ class BluetoothPbapUtils {
     * email or address which is required for updating Secondary Version counter).
     * contactsFieldData - List of field data for phone/email/address.
     * contactId - Contact ID, data1 - field value from data table for phone/email/address*/

    private static void setContactFields(String fieldType, String contactId, String data) {
    @VisibleForTesting
    static void setContactFields(String fieldType, String contactId, String data) {
        ContactData cData;
        if (sContactDataset.containsKey(contactId)) {
            cData = sContactDataset.get(contactId);
+354 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 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.bluetooth.pbap;

import static android.provider.ContactsContract.Data.CONTACT_ID;
import static android.provider.ContactsContract.Data.DATA1;
import static android.provider.ContactsContract.Data.MIMETYPE;

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

import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.res.Resources;
import android.database.MatrixCursor;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.vcard.VCardConfig;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

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

@SmallTest
@RunWith(AndroidJUnit4.class)
public class BluetoothPbapUtilsTest {

    @Mock
    Context mContext;

    @Mock
    Resources mResources;

    @Spy
    BluetoothMethodProxy mProxy;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        BluetoothMethodProxy.setInstanceForTesting(mProxy);

        when(mContext.getResources()).thenReturn(mResources);
        initStaticFields();
    }

    @After
    public void tearDown() {
        BluetoothMethodProxy.setInstanceForTesting(null);
        initStaticFields();
    }

    @Test
    public void checkFieldUpdates_whenSizeAreDifferent_returnsTrue() {
        ArrayList<String> oldFields = new ArrayList<>(List.of("0", "1", "2", "3"));
        ArrayList<String> newFields = new ArrayList<>(List.of("0", "1", "2", "3", "4"));

        assertThat(BluetoothPbapUtils.checkFieldUpdates(oldFields, newFields)).isTrue();
    }

    @Test
    public void checkFieldUpdates_newFieldsHasItsOwnFields_returnsTrue() {
        ArrayList<String> oldFields = new ArrayList<>(List.of("0", "1", "2", "3"));
        ArrayList<String> newFields = new ArrayList<>(List.of("0", "1", "2", "5"));

        assertThat(BluetoothPbapUtils.checkFieldUpdates(oldFields, newFields)).isTrue();
    }

    @Test
    public void checkFieldUpdates_onlyNewFieldsIsNull_returnsTrue() {
        ArrayList<String> oldFields = new ArrayList<>(List.of("0", "1", "2", "3"));
        ArrayList<String> newFields = null;

        assertThat(BluetoothPbapUtils.checkFieldUpdates(oldFields, newFields)).isTrue();
    }

    @Test
    public void checkFieldUpdates_onlyOldFieldsIsNull_returnsTrue() {
        ArrayList<String> oldFields = null;
        ArrayList<String> newFields = new ArrayList<>(List.of("0", "1", "2", "3"));

        assertThat(BluetoothPbapUtils.checkFieldUpdates(oldFields, newFields)).isTrue();
    }

    @Test
    public void checkFieldUpdates_whenBothAreNull_returnsTrue() {
        ArrayList<String> oldFields = null;
        ArrayList<String> newFields = null;

        assertThat(BluetoothPbapUtils.checkFieldUpdates(oldFields, newFields)).isFalse();
    }

    @Test
    public void createFilteredVCardComposer_returnsNewVCardComposer() {
        byte[] filter = new byte[] {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
                (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
        int vcardType = VCardConfig.VCARD_TYPE_V21_GENERIC;

        assertThat(BluetoothPbapUtils.createFilteredVCardComposer(mContext, vcardType, filter))
                .isNotNull();
    }

    @Test
    public void rolloverCounters() {
        BluetoothPbapUtils.sPrimaryVersionCounter = -1;
        BluetoothPbapUtils.sSecondaryVersionCounter = -1;

        BluetoothPbapUtils.rolloverCounters();

        assertThat(BluetoothPbapUtils.sPrimaryVersionCounter).isEqualTo(0);
        assertThat(BluetoothPbapUtils.sSecondaryVersionCounter).isEqualTo(0);
    }

    @Test
    public void setContactFields() {
        String contactId = "1358923";

        BluetoothPbapUtils.setContactFields(BluetoothPbapUtils.TYPE_NAME, contactId,
                "test_name");
        BluetoothPbapUtils.setContactFields(BluetoothPbapUtils.TYPE_PHONE, contactId,
                "0123456789");
        BluetoothPbapUtils.setContactFields(BluetoothPbapUtils.TYPE_EMAIL, contactId,
                "android@android.com");
        BluetoothPbapUtils.setContactFields(BluetoothPbapUtils.TYPE_ADDRESS, contactId,
                "SomeAddress");

        assertThat(BluetoothPbapUtils.sContactDataset.get(contactId)).isNotNull();
    }

    @Test
    public void fetchAndSetContacts_whenCursorIsNull_returnsMinusOne() {
        doReturn(null).when(mProxy).contentResolverQuery(
                any(), any(), any(), any(), any(), any());
        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            assertThat(BluetoothPbapUtils.fetchAndSetContacts(
                    mContext, handler, null, null, null, true))
                    .isEqualTo(-1);
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void fetchAndSetContacts_whenIsLoadTrue_returnsContactsSetSize() {
        MatrixCursor cursor = new MatrixCursor(new String[] {CONTACT_ID, MIMETYPE, DATA1});
        cursor.addRow(new Object[] {"id1", Phone.CONTENT_ITEM_TYPE, "01234567"});
        cursor.addRow(new Object[] {"id1", Email.CONTENT_ITEM_TYPE, "android@android.com"});
        cursor.addRow(new Object[] {"id1", StructuredPostal.CONTENT_ITEM_TYPE, "01234"});
        cursor.addRow(new Object[] {"id2", StructuredName.CONTENT_ITEM_TYPE, "And Roid"});
        cursor.addRow(new Object[] {null, null, null});

        doReturn(cursor).when(mProxy).contentResolverQuery(
                any(), any(), any(), any(), any(), any());
        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            boolean isLoad = true;
            assertThat(BluetoothPbapUtils.fetchAndSetContacts(
                    mContext, handler, null, null, null, isLoad))
                    .isEqualTo(2); // Two IDs exist in sContactSet.
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void fetchAndSetContacts_whenIsLoadFalse_returnsContactsSetSize() {
        MatrixCursor cursor = new MatrixCursor(new String[] {CONTACT_ID, MIMETYPE, DATA1});
        cursor.addRow(new Object[] {"id1", Phone.CONTENT_ITEM_TYPE, "01234567"});
        cursor.addRow(new Object[] {"id1", Email.CONTENT_ITEM_TYPE, "android@android.com"});
        cursor.addRow(new Object[] {"id1", StructuredPostal.CONTENT_ITEM_TYPE, "01234"});
        cursor.addRow(new Object[] {"id2", StructuredName.CONTENT_ITEM_TYPE, "And Roid"});
        cursor.addRow(new Object[] {null, null, null});

        doReturn(cursor).when(mProxy).contentResolverQuery(
                any(), any(), any(), any(), any(), any());
        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            boolean isLoad = false;
            assertThat(BluetoothPbapUtils.fetchAndSetContacts(
                    mContext, handler, null, null, null, isLoad))
                    .isEqualTo(2); // Two IDs exist in sContactSet.
            assertThat(BluetoothPbapUtils.sTotalFields).isEqualTo(1);
            assertThat(BluetoothPbapUtils.sTotalSvcFields).isEqualTo(1);
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void updateSecondaryVersionCounter_whenCursorIsNull_shouldNotCrash() {
        doReturn(null).when(mProxy).contentResolverQuery(
                any(), any(), any(), any(), any(), any());
        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, handler);
        } catch (Exception e) {
            assertWithMessage("Exception should not happen.").fail();
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void updateSecondaryVersionCounter_whenContactsAreAdded() {
        MatrixCursor contactCursor = new MatrixCursor(
                new String[] {Contacts._ID, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP});
        contactCursor.addRow(new Object[] {"id1", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id2", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id3", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id4", Calendar.getInstance().getTimeInMillis()});
        doReturn(contactCursor).when(mProxy).contentResolverQuery(
                any(), eq(Contacts.CONTENT_URI), any(), any(), any(), any());

        MatrixCursor dataCursor = new MatrixCursor(new String[] {CONTACT_ID, MIMETYPE, DATA1});
        dataCursor.addRow(new Object[] {"id1", Phone.CONTENT_ITEM_TYPE, "01234567"});
        dataCursor.addRow(new Object[] {"id1", Email.CONTENT_ITEM_TYPE, "android@android.com"});
        dataCursor.addRow(new Object[] {"id1", StructuredPostal.CONTENT_ITEM_TYPE, "01234"});
        dataCursor.addRow(new Object[] {"id2", StructuredName.CONTENT_ITEM_TYPE, "And Roid"});
        doReturn(dataCursor).when(mProxy).contentResolverQuery(
                any(), eq(Data.CONTENT_URI), any(), any(), any(), any());

        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, handler);

            assertThat(BluetoothPbapUtils.sTotalContacts).isEqualTo(4);
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void updateSecondaryVersionCounter_whenContactsAreDeleted() {
        MatrixCursor contactCursor = new MatrixCursor(
                new String[] {Contacts._ID, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP});
        doReturn(contactCursor).when(mProxy).contentResolverQuery(
                any(), eq(Contacts.CONTENT_URI), any(), any(), any(), any());

        MatrixCursor dataCursor = new MatrixCursor(new String[] {CONTACT_ID, MIMETYPE, DATA1});
        doReturn(dataCursor).when(mProxy).contentResolverQuery(
                any(), eq(Data.CONTENT_URI), any(), any(), any(), any());

        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            BluetoothPbapUtils.sTotalContacts = 2;
            BluetoothPbapUtils.sContactSet.add("id1");
            BluetoothPbapUtils.sContactSet.add("id2");

            BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, handler);

            assertThat(BluetoothPbapUtils.sTotalContacts).isEqualTo(0);
        } finally {
            handlerThread.quit();
        }
    }

    @Test
    public void updateSecondaryVersionCounter_whenContactsAreUpdated() {
        MatrixCursor contactCursor = new MatrixCursor(
                new String[] {Contacts._ID, Contacts.CONTACT_LAST_UPDATED_TIMESTAMP});
        contactCursor.addRow(new Object[] {"id1", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id2", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id3", Calendar.getInstance().getTimeInMillis()});
        contactCursor.addRow(new Object[] {"id4", Calendar.getInstance().getTimeInMillis()});
        doReturn(contactCursor).when(mProxy).contentResolverQuery(
                any(), eq(Contacts.CONTENT_URI), any(), any(), any(), any());

        MatrixCursor dataCursor = new MatrixCursor(new String[] {CONTACT_ID, MIMETYPE, DATA1});
        dataCursor.addRow(new Object[] {"id1", Phone.CONTENT_ITEM_TYPE, "01234567"});
        dataCursor.addRow(new Object[] {"id1", Email.CONTENT_ITEM_TYPE, "android@android.com"});
        dataCursor.addRow(new Object[] {"id1", StructuredPostal.CONTENT_ITEM_TYPE, "01234"});
        dataCursor.addRow(new Object[] {"id2", StructuredName.CONTENT_ITEM_TYPE, "And Roid"});
        doReturn(dataCursor).when(mProxy).contentResolverQuery(
                any(), eq(Data.CONTENT_URI), any(), any(), any(), any());

        HandlerThread handlerThread = new HandlerThread("BluetoothPbapUtilsTest");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());

        try {
            BluetoothPbapUtils.sTotalContacts = 4;

            BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, handler);

            assertThat(BluetoothPbapUtils.sContactDataset).isNotEmpty();
        } finally {
            handlerThread.quit();
        }
    }

    private static void initStaticFields() {
        BluetoothPbapUtils.sPrimaryVersionCounter = 0;
        BluetoothPbapUtils.sSecondaryVersionCounter = 0;
        BluetoothPbapUtils.sContactSet.clear();
        BluetoothPbapUtils.sTotalContacts = 0;
        BluetoothPbapUtils.sTotalFields = 0;
        BluetoothPbapUtils.sTotalSvcFields = 0;
    }
}