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

Commit f3fa7b81 authored by Hemant Gupta's avatar Hemant Gupta Committed by Ajay Panicker
Browse files

OPP: Add OPP 1.2 feature.

Add changes to support OPP 1.2 which uses OBEX over L2CAP.

Test: Connect with Remote OPP Client supporting OPP 1.2 and verify
that connection and transfer happens over L2CAP. Connect with Remote
OPP Client supporting OPP 1.1 and verify that connection and transfer
happens over RFCOMM.

Bug: 33010988
Change-Id: I44c2f2f01fb04f4306d0eb121d66dc08954966c0
parent c09426ef
Loading
Loading
Loading
Loading
+27 −6
Original line number Diff line number Diff line
@@ -80,7 +80,20 @@ public class ObexServerSockets {
     */
    public static ObexServerSockets create(IObexConnectionHandler validator) {
        return create(validator, BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
                BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP);
                BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true);
    }

    /**
     * Creates an Insecure RFCOMM {@link BluetoothServerSocket} and a L2CAP
         *                  {@link BluetoothServerSocket}
     * @param validator a reference to the {@link IObexConnectionHandler} object to call
     *                  to validate an incoming connection.
     * @return a reference to a {@link ObexServerSockets} object instance.
     * @throws IOException if it occurs while creating the {@link BluetoothServerSocket}s.
     */
    public static ObexServerSockets createInsecure(IObexConnectionHandler validator) {
        return create(validator, BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
                BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false);
    }

    /**
@@ -90,14 +103,15 @@ public class ObexServerSockets {
     * {@link #getRfcommChannel()} in {@link ObexServerSockets}.
     * @param validator a reference to the {@link IObexConnectionHandler} object to call
     *                  to validate an incoming connection.
     * @param isSecure boolean flag to determine whther socket would be secured or inseucure.
     * @return a reference to a {@link ObexServerSockets} object instance.
     * @throws IOException if it occurs while creating the {@link BluetoothServerSocket}s.
     *
     * TODO: Make public when it becomes possible to determine that the listen-call
     *       failed due to channel-in-use.
     */
    private static ObexServerSockets create(IObexConnectionHandler validator,
            int rfcommChannel, int l2capPsm) {
    private static ObexServerSockets create(
            IObexConnectionHandler validator, int rfcommChannel, int l2capPsm, boolean isSecure) {
        if(D) Log.d(STAG,"create(rfcomm = " +rfcommChannel + ", l2capPsm = " + l2capPsm +")");
        BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
        if(bt == null) {
@@ -113,10 +127,18 @@ public class ObexServerSockets {
            initSocketOK = true;
            try {
                if(rfcommSocket == null) {
                    if (isSecure) {
                        rfcommSocket = bt.listenUsingRfcommOn(rfcommChannel);
                    } else {
                        rfcommSocket = bt.listenUsingInsecureRfcommOn(rfcommChannel);
                    }
                }
                if(l2capSocket == null) {
                    if (isSecure) {
                        l2capSocket = bt.listenUsingL2capOn(l2capPsm);
                    } else {
                        l2capSocket = bt.listenUsingInsecureL2capOn(l2capPsm);
                    }
                }
            } catch (IOException e) {
                Log.e(STAG, "Error create ServerSockets ",e);
@@ -380,5 +402,4 @@ public class ObexServerSockets {
            }
        }
    }

}
+12 −2
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import javax.obex.ServerRequestHandler;
import javax.obex.ServerSession;

import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.ObexServerSockets;

/**
 * This class runs as an OBEX server
@@ -97,9 +98,13 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen

    boolean mTimeoutMsgSent = false;

    public BluetoothOppObexServerSession(Context context, ObexTransport transport) {
    private ObexServerSockets mServerSocket;

    public BluetoothOppObexServerSession(
            Context context, ObexTransport transport, ObexServerSockets serverSocket) {
        mContext = context;
        mTransport = transport;
        mServerSocket = serverSocket;
        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    }
@@ -583,9 +588,14 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen

    @Override
    public void onClose() {
        if (V) Log.v(TAG, "release WakeLock");
        if (D) Log.d(TAG, "onClose");
        releaseWakeLocks();

        if (mServerSocket != null) {
            if (D) Log.d(TAG, "prepareForNewConnect");
            mServerSocket.prepareForNewConnect();
        }

        /* onClose could happen even before start() where mCallback is set */
        if (mCallback != null) {
            Message msg = Message.obtain(mCallback);
+0 −230
Original line number Diff line number Diff line
/*
 * Copyright (c) 2008-2009, Motorola, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * - Neither the name of the Motorola, Inc. nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.android.bluetooth.opp;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import com.android.bluetooth.BluetoothObexTransport;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * This class listens on OPUSH channel for incoming connection
 */
public class BluetoothOppRfcommListener {
    private static final String TAG = "BtOppRfcommListener";

    private static final boolean V = Constants.VERBOSE;

    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;

    private volatile boolean mInterrupted;

    private Thread mSocketAcceptThread;

    private Handler mCallback;

    private static final int CREATE_RETRY_TIME = 10;

    private final BluetoothAdapter mAdapter;

    private BluetoothServerSocket mBtServerSocket = null;

    private ServerSocket mTcpServerSocket = null;

    public BluetoothOppRfcommListener(BluetoothAdapter adapter) {
        mAdapter = adapter;
    }


    public synchronized boolean start(Handler callback) {
        if (mSocketAcceptThread == null) {
            mCallback = callback;

            mSocketAcceptThread = new Thread(TAG) {

                public void run() {
                    if (Constants.USE_TCP_DEBUG) {
                        try {
                            if (V) Log.v(TAG, "Create TCP ServerSocket");
                            mTcpServerSocket = new ServerSocket(Constants.TCP_DEBUG_PORT, 1);
                        } catch (IOException e) {
                            Log.e(TAG, "Error listing on port" + Constants.TCP_DEBUG_PORT);
                            mInterrupted = true;
                        }
                        while (!mInterrupted) {
                            try {
                                Socket clientSocket = mTcpServerSocket.accept();

                                if (V) Log.v(TAG, "Socket connected!");
                                TestTcpTransport transport = new TestTcpTransport(clientSocket);
                                Message msg = Message.obtain();
                                msg.setTarget(mCallback);
                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
                                msg.obj = transport;
                                msg.sendToTarget();

                            } catch (IOException e) {
                                Log.e(TAG, "Error accept connection " + e);
                            }
                        }
                        if (V) Log.v(TAG, "TCP listen thread finished");
                    } else {
                        boolean serverOK = true;

                        /*
                         * it's possible that create will fail in some cases.
                         * retry for 10 times
                         */
                        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
                            try {
                                if (V) Log.v(TAG, "Starting RFCOMM listener....");
                                mBtServerSocket = mAdapter.listenUsingInsecureRfcommWithServiceRecord("OBEX Object Push", BluetoothUuid.ObexObjectPush.getUuid());
                                if (V) Log.v(TAG, "Started RFCOMM listener....");
                            } catch (IOException e1) {
                                Log.e(TAG, "Error create RfcommServerSocket " + e1);
                                serverOK = false;
                            }

                            if (!serverOK) {
                                synchronized (this) {
                                    try {
                                        if (V) Log.v(TAG, "Wait 300 ms");
                                        Thread.sleep(300);
                                    } catch (InterruptedException e) {
                                        Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
                                        mInterrupted = true;
                                    }
                                }
                            } else {
                                break;
                            }
                        }
                        if (!serverOK) {
                            Log.e(TAG, "Error start listening after " + CREATE_RETRY_TIME + " try");
                            mInterrupted = true;
                        }
                        if (!mInterrupted) {
                            Log.i(TAG, "Accept thread started.");
                        }
                        BluetoothSocket clientSocket;
                        while (!mInterrupted) {
                            try {
                                if (V) Log.v(TAG, "Accepting connection...");
                                if (mBtServerSocket == null) {

                                }
                                BluetoothServerSocket sSocket = mBtServerSocket;
                                if (sSocket ==null) {
                                    mInterrupted = true;

                                } else {
                                    clientSocket = sSocket.accept();
                                    if (V) Log.v(TAG, "Accepted connection from "
                                        + clientSocket.getRemoteDevice());
                                    BluetoothObexTransport transport = new BluetoothObexTransport(
                                        clientSocket);
                                    Message msg = Message.obtain();
                                    msg.setTarget(mCallback);
                                    msg.what = MSG_INCOMING_BTOPP_CONNECTION;
                                    msg.obj = transport;
                                    msg.sendToTarget();
                                }
                            } catch (IOException e) {
                                Log.e(TAG, "Error accept connection " + e);
                                try {
                                    Thread.sleep(500);
                                } catch (InterruptedException ie) {}
                            }
                        }
                        Log.i(TAG, "BluetoothSocket listen thread finished");
                    }
                }
            };
            mInterrupted = false;
            if(!Constants.USE_TCP_SIMPLE_SERVER) {
                mSocketAcceptThread.start();
            }
        }
        return true;
    }

    public synchronized void stop() {
        if (mSocketAcceptThread != null) {
            Log.i(TAG, "stopping Accept Thread");

            mInterrupted = true;
             if (Constants.USE_TCP_DEBUG) {
                if (V) Log.v(TAG, "close mTcpServerSocket");
                if (mTcpServerSocket != null) {
                    try {
                        mTcpServerSocket.close();
                        mTcpServerSocket = null;
                    } catch (IOException e) {
                        Log.e(TAG, "Error close mTcpServerSocket");
                    }
                }
            } else {
                if (V) Log.v(TAG, "close mBtServerSocket");

                if (mBtServerSocket != null) {
                    try {
                        mBtServerSocket.close();
                        mBtServerSocket = null;
                    } catch (IOException e) {
                        Log.e(TAG, "Error close mBtServerSocket");
                    }
                }
            }
            try {
                mSocketAcceptThread.interrupt();
                if (V) Log.v(TAG, "waiting for thread to terminate");
                //mSocketAcceptThread.join(JOIN_TIMEOUT_MS);
                mSocketAcceptThread.join();
                if (V) Log.v(TAG, "done waiting for thread to terminate");
                mSocketAcceptThread = null;
                mCallback = null;
            } catch (InterruptedException e) {
                if (V) Log.v(TAG, "Interrupted waiting for Accept Thread to join");
            }
        }
    }
}
+104 −13
Original line number Diff line number Diff line
@@ -37,7 +37,10 @@ import javax.obex.ObexTransport;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevicePicker;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -55,20 +58,25 @@ import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;
import android.os.Process;

import com.android.bluetooth.BluetoothObexTransport;
import com.android.bluetooth.IObexConnectionHandler;
import com.android.bluetooth.ObexServerSockets;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;

import java.io.IOException;
import java.util.ArrayList;
import com.android.bluetooth.sdp.SdpManager;

/**
 * Performs the background Bluetooth OPP transfer. It also starts thread to
 * accept incoming OPP connection.
 */

public class BluetoothOppService extends ProfileService {
public class BluetoothOppService extends ProfileService implements IObexConnectionHandler {
    private static final boolean D = Constants.DEBUG;
    private static final boolean V = Constants.VERBOSE;

@@ -120,8 +128,6 @@ public class BluetoothOppService extends ProfileService {

    private PowerManager mPowerManager;

    private BluetoothOppRfcommListener mSocketListener;

    private boolean mListenStarted = false;

    private boolean mMediaScanInProgress;
@@ -130,6 +136,8 @@ public class BluetoothOppService extends ProfileService {

    private ObexTransport mPendingConnection = null;

    private int mOppSdpHandle = -1;

    /*
     * TODO No support for queue incoming from multiple devices.
     * Make an array list of server session to support receiving queue from
@@ -145,7 +153,6 @@ public class BluetoothOppService extends ProfileService {
    @Override
    protected void create() {
        if (V) Log.v(TAG, "onCreate");
        mSocketListener = new BluetoothOppRfcommListener(mAdapter);
        mShares = Lists.newArrayList();
        mBatchs = Lists.newArrayList();
        mObserver = new BluetoothShareContentObserver();
@@ -205,6 +212,8 @@ public class BluetoothOppService extends ProfileService {

    private static final int MSG_INCOMING_CONNECTION_RETRY = 4;

    private static final int MSG_INCOMING_BTOPP_CONNECTION = 100;

    private static final int STOP_LISTENER = 200;

    private Handler mHandler = new Handler() {
@@ -212,9 +221,15 @@ public class BluetoothOppService extends ProfileService {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case STOP_LISTENER:
                    if(mSocketListener != null){
                        mSocketListener.stop();
                    }
                    if (mAdapter != null && mOppSdpHandle >= 0
                            && SdpManager.getDefaultManager() != null) {
                        if (D) Log.d(TAG, "Removing SDP record mOppSdpHandle :" + mOppSdpHandle);
                        boolean status =
                                SdpManager.getDefaultManager().removeSdpRecord(mOppSdpHandle);
                        Log.d(TAG, "RemoveSDPrecord returns " + status);
                        mOppSdpHandle = -1;
                    }
                    stopListeners();
                    mListenStarted = false;
                    //Stop Active INBOUND Transfer
                    if(mServerTransfer != null){
@@ -268,9 +283,10 @@ public class BluetoothOppService extends ProfileService {
                        mMediaScanInProgress = false;
                    }
                    break;
                case BluetoothOppRfcommListener.MSG_INCOMING_BTOPP_CONNECTION:
                case MSG_INCOMING_BTOPP_CONNECTION:
                    if (D) Log.d(TAG, "Get incoming connection");
                    ObexTransport transport = (ObexTransport)msg.obj;

                    /*
                     * Strategy for incoming connections:
                     * 1. If there is no ongoing transfer, no on-hold connection, start it
@@ -330,10 +346,19 @@ public class BluetoothOppService extends ProfileService {
        }
    };

    private ObexServerSockets mServerSocket;
    private void startSocketListener() {
        if (V) Log.v(TAG, "start RfcommListener");
        mSocketListener.start(mHandler);
        if (V) Log.v(TAG, "RfcommListener started");
        if (D) Log.d(TAG, "start Socket Listeners");
        stopListeners();
        mServerSocket = ObexServerSockets.createInsecure(this);
        SdpManager sdpManager = SdpManager.getDefaultManager();
        if (sdpManager == null || mServerSocket == null) {
            Log.e(TAG, "ERROR:serversocket object is NULL  sdp manager :" + sdpManager
                            + " mServerSocket:" + mServerSocket);
            return;
        }
        sdpManager.createOppOpsRecord("OBEX Object Push", mServerSocket.getRfcommChannel(),
                mServerSocket.getL2capPsm(), 0x0102, SdpManager.OPP_FORMAT_ALL);
    }

    @Override
@@ -341,8 +366,7 @@ public class BluetoothOppService extends ProfileService {
        if (V) Log.v(TAG, "onDestroy");
        getContentResolver().unregisterContentObserver(mObserver);
        unregisterReceiver(mBluetoothReceiver);
        mSocketListener.stop();

        stopListeners();
        if (mBatchs != null) {
            mBatchs.clear();
        }
@@ -357,7 +381,7 @@ public class BluetoothOppService extends ProfileService {

    /* suppose we auto accept an incoming OPUSH connection */
    private void createServerSession(ObexTransport transport) {
        mServerSession = new BluetoothOppObexServerSession(this, transport);
        mServerSession = new BluetoothOppObexServerSession(this, transport, mServerSocket);
        mServerSession.preStart();
        if (D) Log.d(TAG, "Get ServerSession " + mServerSession.toString()
                    + " for incoming connection" + transport.toString());
@@ -1029,4 +1053,71 @@ public class BluetoothOppService extends ProfileService {
            }
        }
    }

    private void stopListeners() {
        if (mServerSocket != null) {
            mServerSocket.shutdown(false);
            mServerSocket = null;
        }
        if (D) Log.d(TAG, "stopListeners   mServerSocket :" + mServerSocket);
    }

    private BluetoothServerSocket getConnectionSocket(boolean isL2cap) {
        BluetoothServerSocket socket = null;
        boolean socketCreate = false;
        final int CREATE_RETRY_TIME = 10;
        // It's possible that create will fail in some cases. retry for 10 times
        for (int i = 0; i < CREATE_RETRY_TIME; i++) {
            if (D) Log.d(TAG, " CREATE_RETRY_TIME " + i);
            socketCreate = true;
            try {
                socket = (isL2cap)
                        ? mAdapter.listenUsingInsecureL2capOn(
                                  BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP)
                        : mAdapter.listenUsingInsecureRfcommOn(
                                  BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP);
            } catch (IOException e) {
                Log.e(TAG, "Error create ServerSockets ", e);
                socketCreate = false;
            }
            if (!socketCreate) {
                // Need to break out of this loop if BT is being turned off.
                int state = mAdapter.getState();
                if ((state != BluetoothAdapter.STATE_TURNING_ON)
                        && (state != BluetoothAdapter.STATE_ON)) {
                    Log.e(TAG, "initServerSockets failed as Bt State :" + state);
                    break;
                }
                try {
                    if (V) Log.d(TAG, "waiting 300 ms...");
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    Log.e(TAG, "create() was interrupted");
                }
            } else {
                break;
            }
        }
        if (D) Log.d(TAG, " socketCreate :" + socketCreate + " isL2cap :" + isL2cap);
        return socket;
    }

    @Override
    public boolean onConnect(BluetoothDevice device, BluetoothSocket socket) {
        if (D) Log.d(TAG, " onConnect BluetoothSocket :" + socket + " \n :device :" + device);
        BluetoothObexTransport transport = new BluetoothObexTransport(socket);
        Message msg = Message.obtain();
        msg.setTarget(mHandler);
        msg.what = MSG_INCOMING_BTOPP_CONNECTION;
        msg.obj = transport;
        msg.sendToTarget();
        return true;
    }

    @Override
    public void onAcceptFailed() {
        // TODO Auto-generated method stub
        Log.d(TAG, " onAcceptFailed:");
        mHandler.sendMessage(mHandler.obtainMessage(START_LISTENER));
    }
}
+248 −135

File changed.File mode changed from 100755 to 100644.

Preview size limit exceeded, changes collapsed.

Loading