BluetoothMnsObexClient.java revision 70be005a18a35ec5fcb46152f0dfbe82156efa3a
1fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/* 2fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Copyright (C) 2013 Samsung System LSI 3fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Licensed under the Apache License, Version 2.0 (the "License"); 4fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* you may not use this file except in compliance with the License. 5fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* You may obtain a copy of the License at 6fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* 7fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* http://www.apache.org/licenses/LICENSE-2.0 8fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* 9fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* Unless required by applicable law or agreed to in writing, software 10fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* distributed under the License is distributed on an "AS IS" BASIS, 11fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* See the License for the specific language governing permissions and 13fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie* limitations under the License. 14fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie*/ 15fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepackage com.android.bluetooth.map; 16fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 17fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.bluetooth.BluetoothDevice; 18fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.bluetooth.BluetoothSocket; 19fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.content.Context; 20fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Handler; 21fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Looper; 22fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Message; 23fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.ParcelUuid; 24fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.util.Log; 25fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 26fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.BufferedInputStream; 27fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.File; 28fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.FileInputStream; 29fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.IOException; 30fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.OutputStream; 31fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 32fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.ApplicationParameter; 33fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.ClientOperation; 34fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.ClientSession; 35fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.HeaderSet; 36fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.ObexTransport; 37fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport javax.obex.ResponseCodes; 38fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 39fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/** 40fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * The Message Notification Service class runs its own message handler thread, 41fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * to avoid executing long operations on the MAP service Thread. 42fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * This handler context is passed to the content observers, 43fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * hence all call-backs (and thereby transmission of data) is executed 44fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * from this thread. 45fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 46fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xiepublic class BluetoothMnsObexClient extends Thread{ 47fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 48fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static final String TAG = "BluetoothMnsObexClient"; 4970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz private static final boolean D = false; 5070be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz private static final boolean V = false; 51fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 52fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public final static int MSG_SESSION_ERROR = 1; 53fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public final static int MSG_CONNECT_TIMEOUT = 2; 54fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 55fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private ObexTransport mTransport; 56fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private Context mContext; 57fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static Handler mHandler = null; 58fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private volatile boolean mWaitingForRemote; 59fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static final String TYPE_EVENT = "x-bt/MAP-event-report"; 60fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private ClientSession mClientSession; 61fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private boolean mConnected = false; 62fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothDevice mRemoteDevice; 63fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static BluetoothMapContentObserver mObserver; 64fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private boolean mObserverRegistered = false; 65fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 66fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private Looper mLooper = null; 67fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Used by the MAS to forward notification registrations 68fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static final int MSG_MNS_NOTIFICATION_REGISTRATION = 1; 6970be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 70fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 71fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static final ParcelUuid BluetoothUuid_ObexMns = 72fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); 73fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 74fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 75fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public BluetoothMnsObexClient(Context context, BluetoothDevice remoteDevice) { 76fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (remoteDevice == null) { 77fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new NullPointerException("Obex transport is null"); 78fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 79fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mContext = context; 80fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mRemoteDevice = remoteDevice; 81fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 82fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 83fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static Handler getMessageHandler() { 84fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // TODO: if mHandle is null, we should wait for it to be created. 85fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return mHandler; 86fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 87fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 88fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static BluetoothMapContentObserver getContentObserver() { 89fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return mObserver; 90fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 91fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 92fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie @Override 93fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void run() { 94fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Looper.prepare(); 95fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mLooper = Looper.myLooper(); 96fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 97fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 98fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* Create the context observer from within the thread to ensure the "content changed" 99fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * events are handled in this thread. */ 100fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver = new BluetoothMapContentObserver(mContext); 101fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver.init(); 102fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 103fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mHandler = new Handler() { 104fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void handleMessage(Message msg) { 105fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie switch (msg.what) { 106fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie case MSG_MNS_NOTIFICATION_REGISTRATION: 107fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie handleRegistration(msg.arg1 /*masId*/, msg.arg2 /*status*/); 108fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 109fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie default: 110fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie break; 111fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 112fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 113fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie }; 114fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Looper.loop(); 115fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 116fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 117fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public boolean isConnected() { 118fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return mConnected; 119fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 120fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 121fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void disconnect() { 122fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 123fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (mClientSession != null) { 124fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mClientSession.disconnect(null); 125fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "OBEX session disconnected"); 126fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 127fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 128fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG, "OBEX session disconnect error " + e.getMessage()); 129fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 130fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 131fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (mClientSession != null) { 132fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "OBEX session close mClientSession"); 133fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mClientSession.close(); 13470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz mClientSession = null; 135fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "OBEX session closed"); 136fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 137fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 138fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.w(TAG, "OBEX session close error:" + e.getMessage()); 139fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 140fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (mTransport != null) { 141fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 142fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "Close Obex Transport"); 143fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mTransport.close(); 14470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz mTransport = null; 14570be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz mConnected = false; 146fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "Obex Transport Closed"); 147fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 148fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "mTransport.close error: " + e.getMessage()); 149fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 150fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 151fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(mObserverRegistered) { 152fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver.unregisterObserver(); 153fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserverRegistered = false; 154fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 155fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 156fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver.deinit(); 157fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Shut down the thread 158fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(mLooper != null) 159fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mLooper.quit(); 160fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie interrupt(); 161fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 162fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie join(); 163fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (InterruptedException e) { 16470be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz if(V) Log.w(TAG, "got interrupted. Probably a connection shutdown"); 165fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 16670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz mHandler = null; 16770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz mObserver = null; 168fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 169fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 170fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private HeaderSet hsConnect = null; 171fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 172fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void handleRegistration(int masId, int notificationStatus){ 173fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.d(TAG, "handleRegistration( " + masId + ", " + notificationStatus + ")"); 174fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 175fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(isConnected() == false) { 176fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.d(TAG, "handleRegistration: connect"); 177fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie connect(); 178fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 179fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 180fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_NO) { 181fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Unregister - should we disconnect, or keep the connection? - the spec. says nothing about this. 182fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(mObserverRegistered == true) { 183fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver.unregisterObserver(); 184fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserverRegistered = false; 185fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 186fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) { 187fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie /* Connect if we do not have a connection, and start the content observers providing 188fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie * this thread as Handler. 189fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie */ 190fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(mObserverRegistered == false) { 191fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserver.registerObserver(this, masId); 192fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mObserverRegistered = true; 193fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 194fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 195fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 196fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 197fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void connect() { 198fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.d(TAG, "handleRegistration: connect 2"); 199fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 200fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothSocket btSocket = null; 201fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 202fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie btSocket = mRemoteDevice.createInsecureRfcommSocketToServiceRecord( 203fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothUuid_ObexMns.getUuid()); 204fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie btSocket.connect(); 205fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 206fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "BtSocket Connect error " + e.getMessage(), e); 207fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // TODO: do we need to report error somewhere? 208fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return; 209fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 210fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 211fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mTransport = new BluetoothMnsRfcommTransport(btSocket); 212fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 213fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 214fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mClientSession = new ClientSession(mTransport); 215fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mConnected = true; 216fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e1) { 217fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "OBEX session create error " + e1.getMessage()); 218fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 219fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (mConnected && mClientSession != null) { 220fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mConnected = false; 221fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie HeaderSet hs = new HeaderSet(); 222fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // bb582b41-420c-11db-b0de-0800200c9a66 223fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] mnsTarget = { (byte) 0xbb, (byte) 0x58, (byte) 0x2b, (byte) 0x41, 224fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0x42, (byte) 0x0c, (byte) 0x11, (byte) 0xdb, 225fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0xb0, (byte) 0xde, (byte) 0x08, (byte) 0x00, 226fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0x20, (byte) 0x0c, (byte) 0x9a, (byte) 0x66 }; 227fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie hs.setHeader(HeaderSet.TARGET, mnsTarget); 228fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 229fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 230fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = true; 231fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 232fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 233fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie hsConnect = mClientSession.connect(hs); 234fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "OBEX session created"); 235fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mConnected = true; 236fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 237fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "OBEX session connect error " + e.getMessage()); 238fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 239fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 240fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 241fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = false; 242fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 243fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 244fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 245fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public int sendEvent(byte[] eventBytes, int masInstanceId) { 246fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 247fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie boolean error = false; 248fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int responseCode = -1; 249fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie HeaderSet request; 250fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int maxChunkSize, bytesToWrite, bytesWritten = 0; 251fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request = new HeaderSet(); 252fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothMapAppParams appParams = new BluetoothMapAppParams(); 253fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie appParams.setMasInstanceId(masInstanceId); 254fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 255fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ClientOperation putOperation = null; 256fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie OutputStream outputStream = null; 257fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 258fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 259fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request.setHeader(HeaderSet.TYPE, TYPE_EVENT); 260fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request.setHeader(HeaderSet.APPLICATION_PARAMETER, appParams.EncodeParams()); 261fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 262fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request.mConnectionID = new byte[4]; 263fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie System.arraycopy(hsConnect.mConnectionID, 0, request.mConnectionID, 0, 4); 264fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 265fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 266fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = true; 267fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 268fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Send the header first and then the body 269fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 270fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Send headerset Event "); 271fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie putOperation = (ClientOperation)mClientSession.put(request); 272fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // TODO - Should this be kept or Removed 273fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 274fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 275fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when put HeaderSet " + e.getMessage()); 276fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 277fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 278fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 279fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = false; 280fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 281fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (!error) { 282fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 283fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Send headerset Event "); 284fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie outputStream = putOperation.openOutputStream(); 285fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 286fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when opening OutputStream " + e.getMessage()); 287fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 288fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 289fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 290fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 291fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (!error) { 292fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 293fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie maxChunkSize = putOperation.getMaxPacketSize(); 294fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 295fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while (bytesWritten < eventBytes.length) { 296fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie bytesToWrite = Math.min(maxChunkSize, eventBytes.length - bytesWritten); 297fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie outputStream.write(eventBytes, bytesWritten, bytesToWrite); 298fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie bytesWritten += bytesToWrite; 299fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 300fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 301fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (bytesWritten == eventBytes.length) { 302fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "SendEvent finished send length" + eventBytes.length); 303fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie outputStream.close(); 304fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 305fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 30670be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz outputStream.close(); 307fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie putOperation.abort(); 308fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "SendEvent interrupted"); 309fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 310fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 311fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 312fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie handleSendException(e.toString()); 313fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 314fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IndexOutOfBoundsException e) { 315fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie handleSendException(e.toString()); 316fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 317fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } finally { 318fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 319fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (!error) { 320fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie responseCode = putOperation.getResponseCode(); 321fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (responseCode != -1) { 322fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Put response code " + responseCode); 323fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (responseCode != ResponseCodes.OBEX_HTTP_OK) { 324fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "Response error code is " + responseCode); 325fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 326fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 327fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 328fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (putOperation != null) { 329fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie putOperation.close(); 330fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 331fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 332fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when closing stream after send " + e.getMessage()); 333fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 334fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 335fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 336fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return responseCode; 337fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 338fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 339fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private void handleSendException(String exception) { 340fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when sending event: " + exception); 341fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 342fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie} 343