From 6fbf38829532d0368c2ae92b3c3d1b59eb34b80b Mon Sep 17 00:00:00 2001 From: Sanket Agarwal Date: Fri, 19 Jun 2015 11:34:03 -0700 Subject: Mas Obex client should use Handler instead of threads. Using threads has the following issues currently: * Can only handle one enqueue in flight. Makes the client dependent on checking sanity. * Uses interrupts to clean up which can lead to race conditions. Change-Id: Id3b2e2199fea2347dd2b7102ec60004e996fe344 --- .../client/map/BluetoothMasObexClientSession.java | 207 ++++++++++----------- 1 file changed, 101 insertions(+), 106 deletions(-) diff --git a/src/android/bluetooth/client/map/BluetoothMasObexClientSession.java b/src/android/bluetooth/client/map/BluetoothMasObexClientSession.java index f949b8d..9bf75d4 100644 --- a/src/android/bluetooth/client/map/BluetoothMasObexClientSession.java +++ b/src/android/bluetooth/client/map/BluetoothMasObexClientSession.java @@ -17,10 +17,14 @@ package android.bluetooth.client.map; import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; import android.os.Process; import android.util.Log; import java.io.IOException; +import java.lang.ref.WeakReference; import javax.obex.ClientSession; import javax.obex.HeaderSet; @@ -35,97 +39,120 @@ class BluetoothMasObexClientSession { 0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66 }; + private boolean DBG = true; + static final int MSG_OBEX_CONNECTED = 100; static final int MSG_OBEX_DISCONNECTED = 101; static final int MSG_REQUEST_COMPLETED = 102; + private static final int CONNECT = 0; + private static final int DISCONNECT = 1; + private static final int REQUEST = 2; + private final ObexTransport mTransport; private final Handler mSessionHandler; - private ClientThread mClientThread; + private ClientSession mSession; - private volatile boolean mInterrupted; + private HandlerThread mThread; + private Handler mHandler; - private class ClientThread extends Thread { - private final ObexTransport mTransport; + private boolean mConnected; - private ClientSession mSession; + private static class ObexClientHandler extends Handler { + WeakReference mInst; - private BluetoothMasRequest mRequest; + ObexClientHandler(Looper looper, BluetoothMasObexClientSession inst) { + super(looper); + mInst = new WeakReference(inst); + } - private boolean mConnected; + @Override + public void handleMessage(Message msg) { + BluetoothMasObexClientSession inst = mInst.get(); + if (!inst.connected() && msg.what != CONNECT) { + Log.w(TAG, "Cannot execute " + msg + " when not CONNECTED."); + return; + } - public ClientThread(ObexTransport transport) { - super("MAS ClientThread"); + switch (msg.what) { + case CONNECT: + inst.connect(); + break; - mTransport = transport; - mConnected = false; - } + case DISCONNECT: + inst.disconnect(); + break; - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + case REQUEST: + inst.executeRequest((BluetoothMasRequest) msg.obj); + break; + } + } + } - connect(); + public BluetoothMasObexClientSession(ObexTransport transport, Handler handler) { + mTransport = transport; + mSessionHandler = handler; + } - if (mConnected) { - mSessionHandler.obtainMessage(MSG_OBEX_CONNECTED).sendToTarget(); - } else { - mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget(); - return; - } + public void start() { + if (DBG) Log.d(TAG, "start called."); + if (mConnected) { + if (DBG) Log.d(TAG, "Already connected, nothing to do."); + return; + } - while (!mInterrupted) { - synchronized (this) { - if (mRequest == null) { - try { - this.wait(); - } catch (InterruptedException e) { - mInterrupted = true; - } - } - } - - if (!mInterrupted && mRequest != null) { - try { - mRequest.execute(mSession); - } catch (IOException e) { - // this will "disconnect" to cleanup - mInterrupted = true; - } - - BluetoothMasRequest oldReq = mRequest; - mRequest = null; - - mSessionHandler.obtainMessage(MSG_REQUEST_COMPLETED, oldReq).sendToTarget(); - } - } + // Start a thread to handle messages here. + mThread = new HandlerThread("BluetoothMasObexClientSessionThread"); + mThread.start(); + mHandler = new ObexClientHandler(mThread.getLooper(), this); - disconnect(); + // Connect it to the target device via OBEX. + mHandler.obtainMessage(CONNECT).sendToTarget(); + } - mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget(); + public boolean makeRequest(BluetoothMasRequest request) { + if (DBG) Log.d(TAG, "makeRequest called with: " + request); + + boolean status = mHandler.sendMessage(mHandler.obtainMessage(REQUEST, request)); + if (!status) { + Log.e(TAG, "Adding messages failed, state: " + mConnected); + return false; } + return true; + } - private void connect() { - try { - mSession = new ClientSession(mTransport); + public void stop() { + if (DBG) Log.d(TAG, "stop called..."); - HeaderSet headerset = new HeaderSet(); - headerset.setHeader(HeaderSet.TARGET, MAS_TARGET); + mThread.quit(); + disconnect(); + } - headerset = mSession.connect(headerset); + private void connect() { + try { + mSession = new ClientSession(mTransport); - if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK) { - mConnected = true; - } else { - disconnect(); - } - } catch (IOException e) { + HeaderSet headerset = new HeaderSet(); + headerset.setHeader(HeaderSet.TARGET, MAS_TARGET); + + headerset = mSession.connect(headerset); + + if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK) { + mConnected = true; + mSessionHandler.obtainMessage(MSG_OBEX_CONNECTED).sendToTarget(); + } else { + disconnect(); } + } catch (IOException e) { + disconnect(); } + } - private void disconnect() { + private void disconnect() { + if (mSession != null) { try { mSession.disconnect(null); } catch (IOException e) { @@ -135,58 +162,26 @@ class BluetoothMasObexClientSession { mSession.close(); } catch (IOException e) { } - - mConnected = false; - } - - public synchronized boolean schedule(BluetoothMasRequest request) { - if (mRequest != null) { - return false; - } - - mRequest = request; - notify(); - - return true; } - } - public BluetoothMasObexClientSession(ObexTransport transport, Handler handler) { - mTransport = transport; - mSessionHandler = handler; + mConnected = false; + mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget(); } - public void start() { - if (mClientThread == null) { - mClientThread = new ClientThread(mTransport); - mClientThread.start(); - } - - } + private void executeRequest(BluetoothMasRequest request) { + try { + request.execute(mSession); + mSessionHandler.obtainMessage(MSG_REQUEST_COMPLETED, request).sendToTarget(); + } catch (IOException e) { + if (DBG) Log.d(TAG, "Request failed: " + request); - public void stop() { - if (mClientThread != null) { - mClientThread.interrupt(); - - (new Thread() { - @Override - public void run() { - try { - mClientThread.join(); - mClientThread = null; - } catch (InterruptedException e) { - Log.w(TAG, "Interrupted while waiting for thread to join"); - } - } - }).run(); + // Disconnect to cleanup. + disconnect(); } } - public boolean makeRequest(BluetoothMasRequest request) { - if (mClientThread == null) { - return false; - } - return mClientThread.schedule(request); + private boolean connected() { + return mConnected; } } -- cgit v1.2.3