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

Commit 017839b1 authored by hieudz's avatar hieudz Committed by My Name
Browse files

Add BluetoothOppTransferTest

Test: atest BluetoothOppTransferTest
Bug: 244534014
Tag: #refactor
Change-Id: Ib96523623ea5ed5cf7db5938f3c4228490b118f5
parent 7132b71b
Loading
Loading
Loading
Loading
+15 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -33,10 +34,11 @@ import java.io.IOException;
 */
public class BluetoothMethodProxy {
    private static final String TAG = BluetoothMethodProxy.class.getSimpleName();
    private static BluetoothMethodProxy sInstance;
    private static final Object INSTANCE_LOCK = new Object();
    private static BluetoothMethodProxy sInstance;

    private BluetoothMethodProxy() {}
    private BluetoothMethodProxy() {
    }

    /**
     * Get the singleton instance of proxy
@@ -75,6 +77,14 @@ public class BluetoothMethodProxy {
        return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
    }

    /**
     * Proxies {@link ContentResolver#update(Uri, ContentValues, String, String[])}.
     */
    public int contentResolverUpdate(ContentResolver contentResolver, final Uri contentUri,
            final ContentValues contentValues, String where, String[] selectionArgs) {
        return contentResolver.update(contentUri, contentValues, where, selectionArgs);
    }

    /**
     * Proxies {@link ContentResolver#delete(Uri, String, String[])}.
     */
@@ -85,11 +95,10 @@ public class BluetoothMethodProxy {
    }

    /**
     * Proxies {@link ContentResolver#update(Uri, ContentValues, String, String[])}.
     * Proxies {@link BluetoothAdapter#isEnabled()}.
     */
    public int contentResolverUpdate(ContentResolver contentResolver, final Uri contentUri,
            final ContentValues contentValues, String where, String[] selectionArgs) {
        return contentResolver.update(contentUri, contentValues, where, selectionArgs);
    public boolean bluetoothAdapterIsEnabled(BluetoothAdapter adapter) {
        return adapter.isEnabled();
    }

    /**
+15 −7
Original line number Diff line number Diff line
@@ -52,8 +52,10 @@ import android.os.ParcelUuid;
import android.os.Process;
import android.util.Log;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.Utils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.obex.ObexTransport;

import java.io.IOException;
@@ -69,11 +71,14 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch

    private static final boolean V = Constants.VERBOSE;

    private static final int TRANSPORT_ERROR = 10;
    @VisibleForTesting
    static final int TRANSPORT_ERROR = 10;

    private static final int TRANSPORT_CONNECTED = 11;
    @VisibleForTesting
    static final int TRANSPORT_CONNECTED = 11;

    private static final int SOCKET_ERROR_RETRY = 13;
    @VisibleForTesting
    static final int SOCKET_ERROR_RETRY = 13;

    private static final int CONNECT_WAIT_TIMEOUT = 45000;

@@ -202,7 +207,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
    /*
     * Receives events from mConnectThread & mSession back in the main thread.
     */
    private class EventHandler extends Handler {
    @VisibleForTesting
    class EventHandler extends Handler {
        EventHandler(Looper looper) {
            super(looper);
        }
@@ -445,7 +451,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
                        mContext.getContentResolver().delete(info.mUri, null, null);
                    }
                }
                mContext.getContentResolver().update(contentUri, updateValues, null, null);
                BluetoothMethodProxy.getInstance().contentResolverUpdate(
                        mContext.getContentResolver(), contentUri, updateValues, null, null);
                Constants.sendIntentIfCompleted(mContext, contentUri, info.mStatus);
            }
            info = mBatch.getPendingShare();
@@ -480,7 +487,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
         * normally it's impossible to reach here if BT is disabled. Just check
         * for safety
         */
        if (!mAdapter.isEnabled()) {
        if (!BluetoothMethodProxy.getInstance().bluetoothAdapterIsEnabled(mAdapter)) {
            Log.e(TAG, "Can't start transfer when Bluetooth is disabled for " + mBatch.mId);
            markBatchFailed(BluetoothShare.STATUS_UNKNOWN_ERROR);
            mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
@@ -663,7 +670,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch
        }
    }

    private SocketConnectThread mConnectThread;
    @VisibleForTesting
    SocketConnectThread mConnectThread;

    private class SocketConnectThread extends Thread {
        private final String mHost;
+257 −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.opp;

import static com.android.bluetooth.opp.BluetoothOppTransfer.TRANSPORT_CONNECTED;
import static com.android.bluetooth.opp.BluetoothOppTransfer.TRANSPORT_ERROR;

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

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Looper;
import android.os.Message;

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

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.BluetoothObexTransport;
import com.android.obex.ObexTransport;

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

@MediumTest
@RunWith(AndroidJUnit4.class)
public class BluetoothOppTransferTest {
    private final Uri mUri = Uri.parse("file://Idontknow/Justmadeitup");
    private final String mHintString = "this is a object that take 4 bytes";
    private final String mFilename = "random.jpg";
    private final String mMimetype = "image/jpeg";
    private final int mDirection = BluetoothShare.DIRECTION_INBOUND;
    private final String mDestination = "01:23:45:67:89:AB";
    private final int mVisibility = BluetoothShare.VISIBILITY_VISIBLE;
    private final int mConfirm = BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED;
    private final int mStatus = BluetoothShare.STATUS_PENDING;
    private final int mTotalBytes = 1023;
    private final int mCurrentBytes = 42;
    private final int mTimestamp = 123456789;
    private final boolean mMediaScanned = false;

    @Mock
    BluetoothOppObexSession mSession;
    @Spy
    BluetoothMethodProxy mCallProxy = BluetoothMethodProxy.getInstance();
    Context mContext;
    BluetoothOppBatch mBluetoothOppBatch;
    BluetoothOppTransfer mTransfer;
    BluetoothOppTransfer.EventHandler mEventHandler;
    BluetoothOppShareInfo mInitShareInfo;

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

        mInitShareInfo = new BluetoothOppShareInfo(123, mUri, mHintString, mFilename, mMimetype,
                mDirection, mDestination, mVisibility, mConfirm, mStatus, mTotalBytes,
                mCurrentBytes,
                mTimestamp, mMediaScanned);
        mContext = spy(
                new ContextWrapper(
                        InstrumentationRegistry.getInstrumentation().getTargetContext()));
        mBluetoothOppBatch = spy(new BluetoothOppBatch(mContext, mInitShareInfo));
        mTransfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch, mSession);
        mEventHandler = mTransfer.new EventHandler(Looper.getMainLooper());
    }

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

    @Test
    public void onShareAdded_checkFirstPendingShare() {
        BluetoothOppShareInfo newShareInfo = new BluetoothOppShareInfo(1, mUri, mHintString,
                mFilename, mMimetype, BluetoothShare.DIRECTION_INBOUND, mDestination, mVisibility,
                BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED, mStatus, mTotalBytes,
                mCurrentBytes,
                mTimestamp, mMediaScanned);

        doAnswer(invocation -> {
            assertThat((BluetoothOppShareInfo) invocation.getArgument(0))
                    .isEqualTo(mInitShareInfo);
            return null;
        }).when(mSession).addShare(any(BluetoothOppShareInfo.class));

        // This will trigger mTransfer.onShareAdded(), which will call mTransfer
        // .processCurrentShare(),
        // which will add the first pending share to the session
        mBluetoothOppBatch.addShare(newShareInfo);
        verify(mSession).addShare(any(BluetoothOppShareInfo.class));
    }

    @Test
    public void onBatchCanceled_checkStatus() {
        doReturn(0).when(mCallProxy).contentResolverDelete(any(), any(), any(), any());
        // This will trigger mTransfer.onBatchCanceled(),
        // which will then change the status of the batch accordingly
        mBluetoothOppBatch.cancelBatch();
        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FINISHED);
    }

    @Test
    public void start_bluetoothDisabled_batchFail() {
        mTransfer.start();
        // Since Bluetooth is disabled, the batch will fail
        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FAILED);
    }

    @Test
    public void start_receiverRegistered() {
        doReturn(true).when(mCallProxy).bluetoothAdapterIsEnabled(any());
        mTransfer.start();
        verify(mContext).registerReceiver(any(), any(IntentFilter.class));
        // need this, or else the handler thread might throw in middle of the next test
        mTransfer.stop();
    }

    @Test
    public void stop_unregisterRegistered() {
        doReturn(true).when(mCallProxy).bluetoothAdapterIsEnabled(any());
        mTransfer.start();
        mTransfer.stop();
        verify(mContext).unregisterReceiver(any());
    }

    @Test
    public void eventHandler_handleMessage_TRANSPORT_ERROR_connectThreadIsNull() {
        Message message = Message.obtain(mEventHandler, TRANSPORT_ERROR);
        mEventHandler.handleMessage(message);
        assertThat(mTransfer.mConnectThread).isNull();
        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FAILED);
    }

// TODO: try to use ShadowBluetoothDevice
//    @Test
//    public void eventHandler_handleMessage_SOCKET_ERROR_RETRY_connectThreadInitiated() {
//        BluetoothDevice bluetoothDevice = ShadowBluetoothDevice();
//        Message message = Message.obtain(mEventHandler, SOCKET_ERROR_RETRY, bluetoothDevice);
//        mEventHandler.handleMessage(message);
//        assertThat(mTransfer.mConnectThread).isNotNull();
//    }

    @Test
    public void eventHandler_handleMessage_TRANSPORT_CONNECTED_obexSessionStarted() {
        ObexTransport transport = mock(BluetoothObexTransport.class);
        Message message = Message.obtain(mEventHandler, TRANSPORT_CONNECTED, transport);
        mEventHandler.handleMessage(message);
        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_RUNNING);
    }

    @Test
    public void eventHandler_handleMessage_MSG_SHARE_COMPLETE_shareAdded() {
        Message message = Message.obtain(mEventHandler, BluetoothOppObexSession.MSG_SHARE_COMPLETE);

        mInitShareInfo = new BluetoothOppShareInfo(123, mUri, mHintString, mFilename, mMimetype,
                BluetoothShare.DIRECTION_OUTBOUND, mDestination, mVisibility, mConfirm, mStatus,
                mTotalBytes, mCurrentBytes, mTimestamp, mMediaScanned);
        mContext = spy(
                new ContextWrapper(
                        InstrumentationRegistry.getInstrumentation().getTargetContext()));
        mBluetoothOppBatch = spy(new BluetoothOppBatch(mContext, mInitShareInfo));
        mTransfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch, mSession);
        mEventHandler = mTransfer.new EventHandler(Looper.getMainLooper());
        mEventHandler.handleMessage(message);

        // Since there is still a share in mBluetoothOppBatch, it will be added into session
        verify(mSession).addShare(any(BluetoothOppShareInfo.class));
    }

    @Test
    public void eventHandler_handleMessage_MSG_SESSION_COMPLETE_batchFinished() {
        BluetoothOppShareInfo info = mock(BluetoothOppShareInfo.class);
        Message message = Message.obtain(mEventHandler,
                BluetoothOppObexSession.MSG_SESSION_COMPLETE,
                info);
        mEventHandler.handleMessage(message);

        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FINISHED);
    }

    @Test
    public void eventHandler_handleMessage_MSG_SESSION_ERROR_batchFailed() {
        BluetoothOppShareInfo info = mock(BluetoothOppShareInfo.class);
        Message message = Message.obtain(mEventHandler, BluetoothOppObexSession.MSG_SESSION_ERROR,
                info);
        mEventHandler.handleMessage(message);

        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FAILED);
    }

    @Test
    public void eventHandler_handleMessage_MSG_SHARE_INTERRUPTED_batchFailed() {

        mInitShareInfo = new BluetoothOppShareInfo(123, mUri, mHintString, mFilename, mMimetype,
                BluetoothShare.DIRECTION_OUTBOUND, mDestination, mVisibility, mConfirm, mStatus,
                mTotalBytes, mCurrentBytes, mTimestamp, mMediaScanned);
        mBluetoothOppBatch = spy(new BluetoothOppBatch(mContext, mInitShareInfo));
        mTransfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch, mSession);
        mEventHandler = mTransfer.new EventHandler(Looper.getMainLooper());

        BluetoothOppShareInfo info = mock(BluetoothOppShareInfo.class);
        Message message = Message.obtain(mEventHandler,
                BluetoothOppObexSession.MSG_SHARE_INTERRUPTED,
                info);
        mEventHandler.handleMessage(message);

        assertThat(mBluetoothOppBatch.mStatus).isEqualTo(Constants.BATCH_STATUS_FAILED);
    }

    @Test
    public void eventHandler_handleMessage_MSG_CONNECT_TIMEOUT() {
        Message message = Message.obtain(mEventHandler,
                BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
        BluetoothOppShareInfo newInfo = new BluetoothOppShareInfo(321, mUri, mHintString,
                mFilename, mMimetype, mDirection, mDestination, mVisibility, mConfirm, mStatus,
                mTotalBytes, mCurrentBytes, mTimestamp, mMediaScanned);
        // Adding new info will assign value to mCurrentShare
        mBluetoothOppBatch.addShare(newInfo);
        mEventHandler.handleMessage(message);

        verify(mContext).sendBroadcast(argThat(
                arg -> arg.getAction().equals(BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION)));
    }
}