| /* |
| * Copyright (C) 2014 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 android.bluetooth.client.pbap; |
| |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.util.Log; |
| |
| import java.io.IOException; |
| import java.lang.ref.WeakReference; |
| |
| import javax.obex.ClientSession; |
| import javax.obex.HeaderSet; |
| import javax.obex.ObexTransport; |
| import javax.obex.ResponseCodes; |
| |
| final class BluetoothPbapObexSession { |
| private static final boolean DBG = true; |
| private static final String TAG = "BluetoothPbapObexSession"; |
| |
| private static final byte[] PBAP_TARGET = new byte[] { |
| 0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66, |
| 0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66 |
| }; |
| |
| final static int OBEX_SESSION_CONNECTED = 100; |
| final static int OBEX_SESSION_FAILED = 101; |
| final static int OBEX_SESSION_DISCONNECTED = 102; |
| final static int OBEX_SESSION_REQUEST_COMPLETED = 103; |
| final static int OBEX_SESSION_REQUEST_FAILED = 104; |
| final static int OBEX_SESSION_AUTHENTICATION_REQUEST = 105; |
| final static int OBEX_SESSION_AUTHENTICATION_TIMEOUT = 106; |
| |
| final static int MSG_CONNECT = 0; |
| final static int MSG_REQUEST = 1; |
| |
| final static int CONNECTED = 0; |
| final static int CONNECTING = 1; |
| final static int DISCONNECTED = 2; |
| |
| private Handler mSessionHandler; |
| private final ObexTransport mTransport; |
| // private ObexClientThread mObexClientThread; |
| private BluetoothPbapObexAuthenticator mAuth = null; |
| private HandlerThread mThread; |
| private Handler mHandler; |
| private ClientSession mClientSession; |
| |
| private int mState = DISCONNECTED; |
| |
| public BluetoothPbapObexSession(ObexTransport transport) { |
| mTransport = transport; |
| } |
| |
| public synchronized boolean start(Handler handler) { |
| Log.d(TAG, "start"); |
| |
| if (mState == CONNECTED || mState == CONNECTING) { |
| return false; |
| } |
| mState = CONNECTING; |
| mSessionHandler = handler; |
| |
| mAuth = new BluetoothPbapObexAuthenticator(mSessionHandler); |
| |
| // Start the thread to process requests (see {@link schedule()}. |
| mThread = new HandlerThread("BluetoothPbapObexSessionThread"); |
| mThread.start(); |
| mHandler = new ObexClientHandler(mThread.getLooper(), this); |
| |
| // Make connect call non blocking. |
| boolean status = mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT)); |
| if (!status) { |
| mState = DISCONNECTED; |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| public void stop() { |
| if (DBG) { |
| Log.d(TAG, "stop"); |
| } |
| |
| // This will essentially stop the handler and ignore any inflight requests. |
| mThread.quit(); |
| |
| // We clean up the state here. |
| disconnect(false /* no callback */); |
| } |
| |
| public void abort() { |
| stop(); |
| } |
| |
| public boolean schedule(BluetoothPbapRequest request) { |
| if (DBG) { |
| Log.d(TAG, "schedule() called with: " + request); |
| } |
| |
| boolean status = mHandler.sendMessage(mHandler.obtainMessage(MSG_REQUEST, request)); |
| if (!status) { |
| Log.e(TAG, "Adding messages failed, obex must be disconnected."); |
| return false; |
| } |
| return true; |
| } |
| |
| public int isConnected() { |
| return mState; |
| } |
| |
| private void connect() { |
| if (DBG) { |
| Log.d(TAG, "connect()"); |
| } |
| |
| boolean success = true; |
| try { |
| mClientSession = new ClientSession(mTransport); |
| mClientSession.setAuthenticator(mAuth); |
| } catch (IOException e) { |
| Log.d(TAG, "connect() exception: " + e); |
| success = false; |
| } |
| |
| HeaderSet hs = new HeaderSet(); |
| hs.setHeader(HeaderSet.TARGET, PBAP_TARGET); |
| try { |
| hs = mClientSession.connect(hs); |
| |
| if (hs.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) { |
| disconnect(true /* callback */); |
| success = false; |
| } |
| } catch (IOException e) { |
| success = false; |
| } |
| |
| synchronized (this) { |
| if (success) { |
| mSessionHandler.obtainMessage(OBEX_SESSION_CONNECTED).sendToTarget(); |
| mState = CONNECTED; |
| } else { |
| mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget(); |
| mState = DISCONNECTED; |
| } |
| } |
| } |
| |
| private synchronized void disconnect(boolean callback) { |
| if (DBG) { |
| Log.d(TAG, "disconnect()"); |
| } |
| |
| if (mState != DISCONNECTED) { |
| return; |
| } |
| |
| if (mClientSession != null) { |
| try { |
| mClientSession.disconnect(null); |
| mClientSession.close(); |
| } catch (IOException e) { |
| } |
| } |
| |
| if (callback) { |
| mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget(); |
| } |
| |
| mState = DISCONNECTED; |
| } |
| |
| private void executeRequest(BluetoothPbapRequest req) { |
| try { |
| req.execute(mClientSession); |
| } catch (IOException e) { |
| Log.e(TAG, "Error during executeRequest " + e); |
| disconnect(true); |
| } |
| |
| if (req.isSuccess()) { |
| mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_COMPLETED, req).sendToTarget(); |
| } else { |
| mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_FAILED, req).sendToTarget(); |
| } |
| } |
| |
| public boolean setAuthReply(String key) { |
| Log.d(TAG, "setAuthReply key=" + key); |
| |
| if (mAuth == null) { |
| return false; |
| } |
| |
| mAuth.setReply(key); |
| |
| return true; |
| } |
| |
| private static class ObexClientHandler extends Handler { |
| WeakReference<BluetoothPbapObexSession> mInst; |
| |
| ObexClientHandler(Looper looper, BluetoothPbapObexSession inst) { |
| super(looper); |
| mInst = new WeakReference<BluetoothPbapObexSession>(inst); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| BluetoothPbapObexSession inst = mInst.get(); |
| if (inst == null) { |
| Log.e(TAG, "The instance class is no longer around; terminating."); |
| return; |
| } |
| |
| if (inst.isConnected() != CONNECTED && msg.what != MSG_CONNECT) { |
| Log.w(TAG, "Cannot execute " + msg + " when not CONNECTED."); |
| return; |
| } |
| |
| switch (msg.what) { |
| case MSG_CONNECT: |
| inst.connect(); |
| break; |
| case MSG_REQUEST: |
| inst.executeRequest((BluetoothPbapRequest) msg.obj); |
| break; |
| default: |
| Log.e(TAG, "Unknwown message type: " + msg.what); |
| } |
| } |
| } |
| } |
| |