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

Commit 4562c8ce authored by Hieu Dang's avatar Hieu Dang
Browse files

[conflict] Merge changes I0779eb81,Ieb8129bf,I9f821fb1 am: 694a060b

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/2625470



Change-Id: I8f10bacc5d49994d00f6e021fd9bfce3394a2c2c
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Ignore-AOSP-First: Merge conflict resolutions
Test: atest BluetoothInstrumentationTests
parents 0bf7a0de 694a060b
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -258,4 +258,9 @@ public class BluetoothMethodProxy {
            ContextMap map, GattService service) {
        return new AppAdvertiseStats(appUid, id, name, map, service);
    }

    /** Proxies {@link Thread#start()}. */
    public void threadStart(Thread thread) {
        thread.start();
    }
}
+75 −39
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import android.os.Process;
import android.sysprop.BluetoothProperties;
import android.util.Log;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.IObexConnectionHandler;
import com.android.bluetooth.ObexServerSockets;
@@ -130,13 +131,12 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti

    private boolean mPendingUpdate;

    private UpdateThread mUpdateThread;
    @VisibleForTesting UpdateThread mUpdateThread;

    private boolean mUpdateThreadRunning;

    private ArrayList<BluetoothOppShareInfo> mShares;

    private ArrayList<BluetoothOppBatch> mBatches;
    @VisibleForTesting ArrayList<BluetoothOppShareInfo> mShares;
    @VisibleForTesting ArrayList<BluetoothOppBatch> mBatches;

    private BluetoothOppTransfer mTransfer;

@@ -180,9 +180,17 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
                    + BluetoothShare.USER_CONFIRMATION + "="
                    + BluetoothShare.USER_CONFIRMATION_PENDING;

    private static final String WHERE_INVISIBLE_UNCONFIRMED =
            "(" + BluetoothShare.STATUS + " > " + BluetoothShare.STATUS_SUCCESS + " AND "
                    + INVISIBLE + ") OR (" + WHERE_CONFIRM_PENDING_INBOUND + ")";
    @VisibleForTesting
    static final String WHERE_INVISIBLE_UNCONFIRMED =
            "("
                    + BluetoothShare.STATUS
                    + " > "
                    + BluetoothShare.STATUS_SUCCESS
                    + " AND "
                    + INVISIBLE
                    + ") OR ("
                    + WHERE_CONFIRM_PENDING_INBOUND
                    + ")";

    private static BluetoothOppService sBluetoothOppService;

@@ -609,7 +617,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
            mPendingUpdate = true;
            if (mUpdateThread == null) {
                mUpdateThread = new UpdateThread();
                mUpdateThread.start();
                BluetoothMethodProxy.getInstance().threadStart(mUpdateThread);
                mUpdateThreadRunning = true;
            }
        }
@@ -985,10 +993,9 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
        }
    }

    /**
     * Removes the local copy of the info about a share.
     */
    private void deleteShare(int arrayPos) {
    /** Removes the local copy of the info about a share. */
    @VisibleForTesting
    void deleteShare(int arrayPos) {
        BluetoothOppShareInfo info = mShares.get(arrayPos);

        /*
@@ -1112,9 +1119,19 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
    }

    // Run in a background thread at boot.
    private static void trimDatabase(ContentResolver contentResolver) {
    @VisibleForTesting
    static void trimDatabase(ContentResolver contentResolver) {
        // Try-catch is important because trimDatabase can run even when the OPP_PROVIDER is
        // disabled (by OPP service, shell command, etc.).
        // At the sametime, it's ok to retry trimDatabase later when the service restart
        try {
            // remove the invisible/unconfirmed inbound shares
        int delNum = contentResolver.delete(BluetoothShare.CONTENT_URI, WHERE_INVISIBLE_UNCONFIRMED,
            int delNum =
                    BluetoothMethodProxy.getInstance()
                            .contentResolverDelete(
                                    contentResolver,
                                    BluetoothShare.CONTENT_URI,
                                    WHERE_INVISIBLE_UNCONFIRMED,
                                    null);
            if (V) {
                Log.v(TAG, "Deleted shares, number = " + delNum);
@@ -1122,8 +1139,14 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti

            // Keep the latest inbound and successful shares.
            Cursor cursor =
                contentResolver.query(BluetoothShare.CONTENT_URI, new String[]{BluetoothShare._ID},
                        WHERE_INBOUND_SUCCESS, null, BluetoothShare._ID); // sort by id
                    BluetoothMethodProxy.getInstance()
                            .contentResolverQuery(
                                    contentResolver,
                                    BluetoothShare.CONTENT_URI,
                                    new String[] {BluetoothShare._ID},
                                    WHERE_INBOUND_SUCCESS,
                                    null,
                                    BluetoothShare._ID); // sort by id
            if (cursor == null) {
                return;
            }
@@ -1134,14 +1157,22 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
                if (cursor.moveToPosition(numToDelete)) {
                    int columnId = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
                    long id = cursor.getLong(columnId);
                delNum = contentResolver.delete(BluetoothShare.CONTENT_URI,
                        BluetoothShare._ID + " < " + id, null);
                    delNum =
                            BluetoothMethodProxy.getInstance()
                                    .contentResolverDelete(
                                            contentResolver,
                                            BluetoothShare.CONTENT_URI,
                                            BluetoothShare._ID + " < " + id,
                                            null);
                    if (V) {
                        Log.v(TAG, "Deleted old inbound success share: " + delNum);
                    }
                }
            }
            cursor.close();
        } catch (Exception e) {
            Log.e(TAG, "Exception when trimming database: ", e);
        }
    }

    private static class MediaScannerNotifier implements MediaScannerConnectionClient {
@@ -1229,7 +1260,12 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti
    public boolean onConnect(BluetoothDevice device, BluetoothSocket socket) {

        if (D) {
            Log.d(TAG, " onConnect BluetoothSocket :" + socket + " \n :device :" + device.getIdentityAddress());
            Log.d(
                    TAG,
                    " onConnect BluetoothSocket :"
                            + socket
                            + " \n :device :"
                            + device.getIdentityAddress());
        }
        if (!mAcceptNewConnections) {
            Log.d(TAG, " onConnect BluetoothSocket :" + socket + " rejected");
+120 −7
Original line number Diff line number Diff line
@@ -15,23 +15,33 @@
 */
package com.android.bluetooth.opp;

import static com.android.bluetooth.opp.BluetoothOppService.WHERE_INVISIBLE_UNCONFIRMED;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

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

import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.ContentResolver;
import android.database.MatrixCursor;

import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.R;
import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;

import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -39,25 +49,36 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;


@MediumTest
@RunWith(AndroidJUnit4.class)
public class BluetoothOppServiceTest {
    private BluetoothOppService mService = null;
    private BluetoothAdapter mAdapter = null;

    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();
    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();

    @Mock BluetoothMethodProxy mBluetoothMethodProxy;

    @Mock
    private AdapterService mAdapterService;
    @Mock private AdapterService mAdapterService;

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

        BluetoothMethodProxy.setInstanceForTesting(mBluetoothMethodProxy);
        // BluetoothOppService can create a UpdateThread, which will call
        // BluetoothOppNotification#updateNotification(), which in turn create a new
        // NotificationUpdateThread. Both threads may cause the tests to fail because they try to
        // access to ContentProvider in multiple places (ContentProvider might be disabled & there
        // is no mocking). Since we have no intention to test those threads, avoid running them
        doNothing().when(mBluetoothMethodProxy).threadStart(any());

        TestUtils.setAdapterService(mAdapterService);
        doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
        TestUtils.startService(mServiceRule, BluetoothOppService.class);
        mService = BluetoothOppService.getBluetoothOppService();

        Assert.assertNotNull(mService);
        // Try getting the Bluetooth adapter
        mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -66,6 +87,11 @@ public class BluetoothOppServiceTest {

    @After
    public void tearDown() throws Exception {
        // Since the update thread is not run (we mocked it), it will not clean itself on interrupt
        // (normally, the service will wait for the update thread to clean itself after
        // being interrupted). We clean it manually here
        mService.mUpdateThread = null;
        BluetoothMethodProxy.setInstanceForTesting(null);
        TestUtils.stopService(mServiceRule, BluetoothOppService.class);
        TestUtils.clearAdapterService(mAdapterService);
    }
@@ -74,5 +100,92 @@ public class BluetoothOppServiceTest {
    public void testInitialize() {
        Assert.assertNotNull(BluetoothOppService.getBluetoothOppService());
    }

    @Test
    public void deleteShare_deleteShareAndCorrespondingBatch() {
        int infoTimestamp = 123456789;
        int infoTimestamp2 = 123489;

        BluetoothOppShareInfo shareInfo = mock(BluetoothOppShareInfo.class);
        shareInfo.mTimestamp = infoTimestamp;
        shareInfo.mDestination = "AA:BB:CC:DD:EE:FF";
        BluetoothOppShareInfo shareInfo2 = mock(BluetoothOppShareInfo.class);
        shareInfo2.mTimestamp = infoTimestamp2;
        shareInfo2.mDestination = "00:11:22:33:44:55";

        mService.mShares.clear();
        mService.mShares.add(shareInfo);
        mService.mShares.add(shareInfo2);

        // batch1 will be removed
        BluetoothOppBatch batch1 = new BluetoothOppBatch(mService, shareInfo);
        BluetoothOppBatch batch2 = new BluetoothOppBatch(mService, shareInfo2);
        batch2.mStatus = Constants.BATCH_STATUS_FINISHED;
        mService.mBatches.clear();
        mService.mBatches.add(batch1);
        mService.mBatches.add(batch2);

        mService.deleteShare(0);
        assertThat(mService.mShares.size()).isEqualTo(1);
        assertThat(mService.mBatches.size()).isEqualTo(1);
        assertThat(mService.mShares.get(0)).isEqualTo(shareInfo2);
        assertThat(mService.mBatches.get(0)).isEqualTo(batch2);
    }

    @Test
    public void dump_shouldNotThrow() {
        BluetoothOppShareInfo info = mock(BluetoothOppShareInfo.class);

        mService.mShares.add(info);

        // should not throw
        mService.dump(new StringBuilder());
    }

    @Test
    public void trimDatabase_trimsOldOrInvisibleRecords() {
        ContentResolver contentResolver =
                InstrumentationRegistry.getInstrumentation()
                        .getTargetContext()
                        .getContentResolver();

        doReturn(1 /* any int is Ok */)
                .when(mBluetoothMethodProxy)
                .contentResolverDelete(
                        eq(contentResolver), eq(BluetoothShare.CONTENT_URI), anyString(), any());

        MatrixCursor cursor = new MatrixCursor(new String[] {BluetoothShare._ID}, 500);
        for (long i = 0; i < Constants.MAX_RECORDS_IN_DATABASE + 20; i++) {
            cursor.addRow(new Object[] {i});
        }

        doReturn(cursor)
                .when(mBluetoothMethodProxy)
                .contentResolverQuery(
                        eq(contentResolver),
                        eq(BluetoothShare.CONTENT_URI),
                        any(),
                        any(),
                        any(),
                        any());

        BluetoothOppService.trimDatabase(contentResolver);

        // check trimmed invisible records
        verify(mBluetoothMethodProxy)
                .contentResolverDelete(
                        eq(contentResolver),
                        eq(BluetoothShare.CONTENT_URI),
                        eq(WHERE_INVISIBLE_UNCONFIRMED),
                        any());

        // check trimmed old records
        verify(mBluetoothMethodProxy)
                .contentResolverDelete(
                        eq(contentResolver),
                        eq(BluetoothShare.CONTENT_URI),
                        eq(BluetoothShare._ID + " < " + 20),
                        any());
    }
}