1192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta/* 2192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Copyright (C) 2014 The Android Open Source Project 3192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 4192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * you may not use this file except in compliance with the License. 6192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * You may obtain a copy of the License at 7192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 8192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * http://www.apache.org/licenses/LICENSE-2.0 9192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * 10192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Unless required by applicable law or agreed to in writing, software 11192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * See the License for the specific language governing permissions and 14192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * limitations under the License. 15192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 16192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 17192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptapackage android.bluetooth.client.pbap; 18192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 19192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.os.Handler; 20192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.Log; 21192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 22192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.IOException; 23192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 24192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ClientSession; 25192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.HeaderSet; 26192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ObexTransport; 27192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ResponseCodes; 28192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 29192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptafinal class BluetoothPbapObexSession { 30192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private static final String TAG = "BluetoothPbapObexSession"; 31192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 32192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private static final byte[] PBAP_TARGET = new byte[] { 33192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66, 34192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66 35192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta }; 36192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 37192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_CONNECTED = 100; 38192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_FAILED = 101; 39192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_DISCONNECTED = 102; 40192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_REQUEST_COMPLETED = 103; 41192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_REQUEST_FAILED = 104; 42192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_AUTHENTICATION_REQUEST = 105; 43192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta final static int OBEX_SESSION_AUTHENTICATION_TIMEOUT = 106; 44192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 45192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private Handler mSessionHandler; 46192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private final ObexTransport mTransport; 47192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private ObexClientThread mObexClientThread; 48192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BluetoothPbapObexAuthenticator mAuth = null; 49192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 50192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public BluetoothPbapObexSession(ObexTransport transport) { 51192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mTransport = transport; 52192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 53192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 54192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void start(Handler handler) { 55192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "start"); 56192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler = handler; 57192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 58192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mAuth = new BluetoothPbapObexAuthenticator(mSessionHandler); 59192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 60192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread = new ObexClientThread(); 61192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread.start(); 62192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 63192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 64192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void stop() { 65192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "stop"); 66192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 67192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mObexClientThread != null) { 68192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 69192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread.interrupt(); 70192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread.join(); 71192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread = null; 72192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (InterruptedException e) { 73192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 74192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 75192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 76192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 77192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void abort() { 78192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "abort"); 79192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 80192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mObexClientThread != null && mObexClientThread.mRequest != null) { 81192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta /* 82192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * since abort may block until complete GET is processed inside OBEX 83192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * session, let's run it in separate thread so it won't block UI 84192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */ 85192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta (new Thread() { 86192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 87192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void run() { 88192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mObexClientThread.mRequest.abort(); 89192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 90192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta }).run(); 91192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 92192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 93192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 94192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public boolean schedule(BluetoothPbapRequest request) { 95192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "schedule: " + request.getClass().getSimpleName()); 96192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 97192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mObexClientThread == null) { 98192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.e(TAG, "OBEX session not started"); 99192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 100192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 101192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 102192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return mObexClientThread.schedule(request); 103192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 104192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 105192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public boolean setAuthReply(String key) { 106192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "setAuthReply key=" + key); 107192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 108192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mAuth == null) { 109192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 110192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 111192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 112192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mAuth.setReply(key); 113192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 114192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return true; 115192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 116192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 117192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private class ObexClientThread extends Thread { 118192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 119192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private static final String TAG = "ObexClientThread"; 120192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 121192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private ClientSession mClientSession; 122192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private BluetoothPbapRequest mRequest; 123192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 124192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private volatile boolean mRunning = true; 125192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 126192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public ObexClientThread() { 127192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 128192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mClientSession = null; 129192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRequest = null; 130192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 131192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 132192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta @Override 133192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public void run() { 134192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta super.run(); 135192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 136192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (!connect()) { 137192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler.obtainMessage(OBEX_SESSION_FAILED).sendToTarget(); 138192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return; 139192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 140192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 141192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler.obtainMessage(OBEX_SESSION_CONNECTED).sendToTarget(); 142192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 143192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta while (mRunning) { 144192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta synchronized (this) { 145192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 146192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mRequest == null) { 147192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta this.wait(); 148192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 149192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (InterruptedException e) { 150192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRunning = false; 151192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta break; 152192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 153192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 154192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 155192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mRunning && mRequest != null) { 156192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 157192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRequest.execute(mClientSession); 158192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 159192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta // this will "disconnect" for cleanup 160192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRunning = false; 161192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 162192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 163192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mRequest.isSuccess()) { 164192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_COMPLETED, mRequest) 165192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta .sendToTarget(); 166192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } else { 167192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_FAILED, mRequest) 168192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta .sendToTarget(); 169192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 170192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 171192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 172192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRequest = null; 173192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 174192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 175192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta disconnect(); 176192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 177192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget(); 178192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 179192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 180192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta public synchronized boolean schedule(BluetoothPbapRequest request) { 181192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "schedule: " + request.getClass().getSimpleName()); 182192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 183192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mRequest != null) { 184192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 185192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 186192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 187192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mRequest = request; 188192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta notify(); 189192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 190192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return true; 191192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 192192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 193192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private boolean connect() { 194192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "connect"); 195192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 196192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 197192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mClientSession = new ClientSession(mTransport); 198192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mClientSession.setAuthenticator(mAuth); 199192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 200192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 201192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 202192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 203192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta HeaderSet hs = new HeaderSet(); 204192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta hs.setHeader(HeaderSet.TARGET, PBAP_TARGET); 205192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 206192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 207192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta hs = mClientSession.connect(hs); 208192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 209192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (hs.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) { 210192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta disconnect(); 211192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 212192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 213192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 214192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return false; 215192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 216192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 217192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta return true; 218192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 219192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 220192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta private void disconnect() { 221192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta Log.d(TAG, "disconnect"); 222192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta 223192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta if (mClientSession != null) { 224192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta try { 225192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mClientSession.disconnect(null); 226192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta mClientSession.close(); 227192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } catch (IOException e) { 228192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 229192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 230192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 231192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta } 232192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta} 233