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

Commit 5a8e6dc6 authored by Sal Savage's avatar Sal Savage
Browse files

Combine FakeObexServer and FakeObexTransport for simpler syntax

Bug: 365626536
Flag: EXEMPT, test only change
Test: atest BluetoothInstrumentationTests
Change-Id: I76b2e9491cffdd84b8afe0fae98fe8884a73a7bc
parent 733678d3
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ public class RequestTest {
    @Before
    public void setUp() throws IOException {
        mFakeMapObexServer = new FakeMapObexServer();
        mFakeClientSession = new ClientSession(mFakeMapObexServer.mClientObexTransport);
        mFakeClientSession = mFakeMapObexServer.getClientSession();
        mFakeClientSession.connect(new HeaderSet());
    }

@@ -182,7 +182,7 @@ public class RequestTest {

        @Override
        @SuppressWarnings("JavaUtilDate")
        public int onGetValidator(final Operation op) {
        public int onGet(final Operation op) {
            OutputStream outputStream;
            HeaderSet replyHeaders = new HeaderSet();
            BluetoothMapAppParams outAppParams = new BluetoothMapAppParams();
@@ -223,7 +223,7 @@ public class RequestTest {
        }

        @Override
        public int onPutValidator(final Operation op) {
        public int onPut(final Operation op) {
            try {
                HeaderSet request = op.getReceivedHeader();
                String type = (String) request.getHeader(HeaderSet.TYPE);
@@ -263,7 +263,7 @@ public class RequestTest {
        }

        @Override
        public int onSetPathValidator(
        public int onSetPath(
                final HeaderSet request,
                HeaderSet reply,
                final boolean backup,
+237 −29
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth;

import com.android.obex.ClientSession;
import com.android.obex.HeaderSet;
import com.android.obex.ObexTransport;
import com.android.obex.Operation;
@@ -23,61 +24,266 @@ import com.android.obex.ResponseCodes;
import com.android.obex.ServerRequestHandler;
import com.android.obex.ServerSession;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/** A Fake OBEX Server base class for use when testing OBEX clients and client requests. */
public class FakeObexServer {
    private static final String TAG = FakeObexServer.class.getSimpleName();

    // Server streams to talk with client under test
    private final PipedInputStream mReceiveFromClient;
    private final PipedOutputStream mSendToClient;

    // Client streams to talk to server
    private final PipedInputStream mReceiveFromServer;
    private final PipedOutputStream mSendToServer;

    // Transports for either side
    private final TestObexTransport mServerTransport;
    private final TestObexTransport mClientTransport;

    private final TestServiceRequestHandler mRequestHandler;
    private final ServerSession mServerSession;

    public FakeObexServer() throws IOException {

        mSendToServer = new PipedOutputStream();
        mReceiveFromServer = new PipedInputStream();
        mReceiveFromClient = new PipedInputStream(mSendToServer);
        mSendToClient = new PipedOutputStream(mReceiveFromServer);

        // Transports
        mServerTransport = new TestObexTransport(mReceiveFromClient, mSendToClient, true);
        mClientTransport = new TestObexTransport(mReceiveFromServer, mSendToServer, true);

        mRequestHandler = new TestServiceRequestHandler(this);
        mServerSession = new ServerSession(mServerTransport, mRequestHandler, null);
    }

    /**
     * Get a transport for use with a client.
     *
     * <p>You can use the openInputStream() and openOutputStream() to get the underlying stream
     * objects and inject them into your objects under test.
     */
    public ObexTransport getClientTransport() {
        return mClientTransport;
    }

    /**
 * A fake obex server for testing obex clients. Test cases should implement *Validator functions to
 * validate input and return appropriate responses for individual tests.
     * Directly create a session with this server.
     *
 * <p>Note: it is important to not perform any testing Assert operations within the validators as
 * that would crash the testing framework.
     * <p>This can be used to quickly test request objects that need a ClientSession
     */
public abstract class FakeObexServer {
    public ClientSession getClientSession() throws IOException {
        return new ClientSession(mClientTransport);
    }

    public ObexTransport mClientObexTransport;
    /**
     * This will close the underlying transport, which will close the streams given to us.
     *
     * <p>By specification, servers themselves cannot issue an OBEX session level disconnect.
     */
    public void close() {
        mServerSession.close();
    }

    private ObexTransport mServerObexTransport;
    private Server mFakeServer;
    private FakeObexTransport mFakeObexTransport;
    // *********************************************************************************************
    // * Server Operations
    // *********************************************************************************************

    public FakeObexServer() throws IOException {
        mFakeServer = new Server();
        mFakeObexTransport = new FakeObexTransport();
        mServerObexTransport = mFakeObexTransport.mServerTransport;
        mClientObexTransport = mFakeObexTransport.mClientTransport;
        new ServerSession(mServerObexTransport, mFakeServer, null);
    public int onConnect(final HeaderSet request, HeaderSet reply) {
        return ResponseCodes.OBEX_HTTP_OK;
    }

    public void onDisconnect(final HeaderSet request, HeaderSet reply) {}

    public int onGet(final Operation op) {
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
    }

    public int onPut(final Operation op) {
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
    }

    public int onAbort(final HeaderSet request, HeaderSet reply) {
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
    }

    public int onSetPath(
            final HeaderSet request, HeaderSet reply, final boolean backup, final boolean create) {
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
    }

    public void onClose() {}

    /**
     * Send a response to a client with the given headers and payload
     *
     * @param op The Operation object representing the ongoing operation with the client
     * @param replyHeaders The HeaderSet to return to the client
     * @param bytes The payload to send in the response, if any
     */
    public final int sendResponse(Operation op, HeaderSet replyHeaders, byte[] bytes) {
        int responseCode = ResponseCodes.OBEX_HTTP_OK;
        OutputStream outStream = null;
        int maxChunkSize = 0;
        int bytesToWrite = 0;
        int bytesWritten = 0;

        try {
            op.sendHeaders(replyHeaders); // Do this before getting chunk size
            outStream = op.openOutputStream();
            if (bytes == null) {
                op.noBodyHeader();
                outStream.flush();
            } else {
                maxChunkSize = op.getMaxPacketSize();
                while (bytesWritten < bytes.length) {
                    bytesToWrite = Math.min(maxChunkSize, bytes.length - bytesWritten);
                    outStream.write(bytes, bytesWritten, bytesToWrite);
                    bytesWritten += bytesToWrite;
                }
            }
        } catch (IOException e) {
            responseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        } finally {
            // Make sure we close
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (IOException e) {
                    // drop, as we're closing anyways
                }
            }
        }
        // If we didn't write everything then send the error code
        if (bytes != null && bytesWritten != bytes.length) {
            responseCode = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
        }
        // Otherwise, success!
        return responseCode;
    }

    // *********************************************************************************************
    // * Transport
    // *********************************************************************************************

    public static class TestObexTransport implements ObexTransport {

        private final InputStream mInput;
        private final OutputStream mOutput;
        private boolean mIsSrmSupported;

        public TestObexTransport(InputStream input, OutputStream output, boolean isSrmSupported) {
            mInput = input;
            mOutput = output;
            setSrmSupported(isSrmSupported);
        }

        @Override
        public DataInputStream openDataInputStream() throws IOException {
            return new DataInputStream(openInputStream());
        }

        @Override
        public DataOutputStream openDataOutputStream() throws IOException {
            return new DataOutputStream(openOutputStream());
        }

        @Override
        public InputStream openInputStream() throws IOException {
            return mInput;
        }

        @Override
        public OutputStream openOutputStream() throws IOException {
            return mOutput;
        }

        @Override
        public int getMaxReceivePacketSize() {
            return -1;
        }

        @Override
        public int getMaxTransmitPacketSize() {
            return -1;
        }

        @Override
        public void connect() throws IOException {}

        @Override
        public void create() throws IOException {}

        @Override
        public void disconnect() throws IOException {}

        @Override
        public void listen() throws IOException {}

        @Override
        public void close() throws IOException {
            mInput.close();
            mOutput.close();
        }

        public boolean isSrmSupported() {
            return mIsSrmSupported;
        }

    public abstract int onGetValidator(Operation op);
        public void setSrmSupported(boolean isSrmSupported) {
            mIsSrmSupported = isSrmSupported;
        }
    }

    public abstract int onPutValidator(Operation op);
    // *********************************************************************************************
    // * Request Handler
    // *********************************************************************************************

    public abstract int onSetPathValidator(
            HeaderSet request, HeaderSet reply, boolean backup, boolean create);
    /**
     * Internal ServerRequestHandler that delegates calls to the FakeObexServer implementation
     *
     * <p>This is setup this way for easier test syntax, so one can extend the fake without needing
     * to care about the framework specifics
     */
    private static class TestServiceRequestHandler extends ServerRequestHandler {
        private final FakeObexServer mServer;

    class Server extends ServerRequestHandler {
        public TestServiceRequestHandler(FakeObexServer server) {
            mServer = server;
        }

        @Override
        public int onConnect(final HeaderSet request, HeaderSet reply) {
            return ResponseCodes.OBEX_HTTP_OK;
            return mServer.onConnect(request, reply);
        }

        @Override
        public void onDisconnect(final HeaderSet request, HeaderSet reply) {}
        public void onDisconnect(final HeaderSet request, HeaderSet reply) {
            mServer.onDisconnect(request, reply);
        }

        @Override
        public int onGet(final Operation op) {
            return onGetValidator(op);
            return mServer.onGet(op);
        }

        @Override
        public int onPut(final Operation op) {
            return onPutValidator(op);
            return mServer.onPut(op);
        }

        @Override
        public int onAbort(final HeaderSet request, HeaderSet reply) {
            return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
            return mServer.onAbort(request, reply);
        }

        @Override
@@ -86,10 +292,12 @@ public abstract class FakeObexServer {
                HeaderSet reply,
                final boolean backup,
                final boolean create) {
            return onSetPathValidator(request, reply, backup, create);
            return mServer.onSetPath(request, reply, backup, create);
        }

        @Override
        public void onClose() {}
        public void onClose() {
            mServer.onClose();
        }
    }
}
+0 −117
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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;

import com.android.obex.ObexTransport;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

/**
 * A fake obex transport used for testing Client/Server connections. The transport uses two pairs of
 * pipes to route input from the client to the server, and back. The obex transport is of the
 * simplest form, returning default values for everything.
 */
public class FakeObexTransport {
    ObexTransport mClientTransport;
    ObexTransport mServerTransport;

    PipedInputStream mClientInputStream;
    PipedInputStream mServerInputStream;
    PipedOutputStream mClientOutputStream;
    PipedOutputStream mServerOutputStream;

    public FakeObexTransport() throws IOException {
        mClientInputStream = new PipedInputStream();
        mServerOutputStream = new PipedOutputStream(mClientInputStream);
        mServerInputStream = new PipedInputStream();
        mClientOutputStream = new PipedOutputStream(mServerInputStream);

        mClientTransport = new BiDirectionalTransport(mClientInputStream, mClientOutputStream);
        mServerTransport = new BiDirectionalTransport(mServerInputStream, mServerOutputStream);
    }

    static class BiDirectionalTransport implements ObexTransport {

        InputStream mInputStream;
        OutputStream mOutputStream;

        BiDirectionalTransport(InputStream inputStream, OutputStream outputStream) {
            mInputStream = inputStream;
            mOutputStream = outputStream;
        }

        @Override
        public DataInputStream openDataInputStream() throws IOException {
            return new DataInputStream(openInputStream());
        }

        @Override
        public DataOutputStream openDataOutputStream() throws IOException {
            return new DataOutputStream(openOutputStream());
        }

        @Override
        public InputStream openInputStream() throws IOException {
            return mInputStream;
        }

        @Override
        public OutputStream openOutputStream() throws IOException {
            return mOutputStream;
        }

        @Override
        public void connect() throws IOException {}

        @Override
        public void create() throws IOException {}

        @Override
        public void disconnect() throws IOException {}

        @Override
        public void listen() throws IOException {}

        @Override
        public void close() throws IOException {}

        public boolean isConnected() throws IOException {
            return true;
        }

        @Override
        public int getMaxTransmitPacketSize() {
            return -1;
        }

        @Override
        public int getMaxReceivePacketSize() {
            return -1;
        }

        @Override
        public boolean isSrmSupported() {
            return true;
        }
    }
}