1fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie/* 2326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde* Copyright (C) 2014 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; 19bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpMnsRecord; 20fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Handler; 2170bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xuimport android.os.HandlerThread; 22fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Looper; 23fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.Message; 24fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.os.ParcelUuid; 25fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport android.util.Log; 26326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bondeimport android.util.SparseBooleanArray; 27fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 28bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport com.android.bluetooth.BluetoothObexTransport; 29bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 30fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.IOException; 31fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xieimport java.io.OutputStream; 32fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 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 */ 4670bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xupublic class BluetoothMnsObexClient { 47fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 48fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static final String TAG = "BluetoothMnsObexClient"; 49326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private static final boolean D = BluetoothMapService.DEBUG; 50326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private static final boolean V = BluetoothMapService.VERBOSE; 51fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 52fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private ObexTransport mTransport; 5370bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu public Handler mHandler = null; 54fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private volatile boolean mWaitingForRemote; 55fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private static final String TYPE_EVENT = "x-bt/MAP-event-report"; 56fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private ClientSession mClientSession; 57fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private boolean mConnected = false; 58fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothDevice mRemoteDevice; 59326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private SparseBooleanArray mRegisteredMasIds = new SparseBooleanArray(1); 60326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 61326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private HeaderSet mHsConnect = null; 62c09b531ba47eee740485b0c6022981fc38ef1587Zhihai Xu private Handler mCallback = null; 63bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private final SdpMnsRecord mMnsRecord; 64fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Used by the MAS to forward notification registrations 65fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public static final int MSG_MNS_NOTIFICATION_REGISTRATION = 1; 66326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde public static final int MSG_MNS_SEND_EVENT = 2; 6770be005a18a35ec5fcb46152f0dfbe82156efa3aKim Schulz 68fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 69326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde public static final ParcelUuid BLUETOOTH_UUID_OBEX_MNS = 70fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); 71fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 72fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 73bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public BluetoothMnsObexClient(BluetoothDevice remoteDevice, 74bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpMnsRecord mnsRecord, Handler callback) { 75fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (remoteDevice == null) { 76fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie throw new NullPointerException("Obex transport is null"); 77fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 78326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRemoteDevice = remoteDevice; 7970bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu HandlerThread thread = new HandlerThread("BluetoothMnsObexClient"); 8070bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu thread.start(); 81326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde /* This will block until the looper have started, hence it will be safe to use it, 82326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde when the constructor completes */ 8370bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu Looper looper = thread.getLooper(); 8470bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu mHandler = new MnsObexClientHandler(looper); 85c09b531ba47eee740485b0c6022981fc38ef1587Zhihai Xu mCallback = callback; 86bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mMnsRecord = mnsRecord; 87fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 88fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 8970bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu public Handler getMessageHandler() { 90fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return mHandler; 91fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 92fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 9370bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu private final class MnsObexClientHandler extends Handler { 9470bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu private MnsObexClientHandler(Looper looper) { 9570bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu super(looper); 9670bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu } 97fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 9870bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu @Override 9970bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu public void handleMessage(Message msg) { 10070bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu switch (msg.what) { 10170bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu case MSG_MNS_NOTIFICATION_REGISTRATION: 10270bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu handleRegistration(msg.arg1 /*masId*/, msg.arg2 /*status*/); 10370bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu break; 104326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde case MSG_MNS_SEND_EVENT: 105326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde sendEventHandler((byte[])msg.obj/*byte[]*/, msg.arg1 /*masId*/); 106326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde break; 10770bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu default: 10870bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu break; 109fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 11070bfe3280ce158c39dbb25fe18386f0d10b490d3Zhihai Xu } 111fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 112fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 113fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public boolean isConnected() { 114fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return mConnected; 115fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 116fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 117824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu /** 118824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu * Disconnect the connection to MNS server. 119824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu * Call this when the MAS client requests a de-registration on events. 120824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu */ 1210e7e149687b0b5e340991b20c9d8e5232e8d3e39Hemant Gupta public synchronized 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 } 151824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu } 152824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu 153824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu /** 154824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu * Shutdown the MNS. 155824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu */ 156824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu public void shutdown() { 157824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu /* should shutdown handler thread first to make sure 158326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * handleRegistration won't be called when disconnect 159824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu */ 160824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu if (mHandler != null) { 161824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu // Shut down the thread 162824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu mHandler.removeCallbacksAndMessages(null); 163824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu Looper looper = mHandler.getLooper(); 164824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu if (looper != null) { 165824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu looper.quit(); 166824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu } 167824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu mHandler = null; 168824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu } 169824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu 170824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu /* Disconnect if connected */ 171824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu disconnect(); 172824929471ee80476e6d6774eedac9f30c5623eb2Zhihai Xu 173326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRegisteredMasIds.clear(); 174fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 175fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 176326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde /** 177326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * We store a list of registered MasIds only to control connect/disconnect 178326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * @param masId 179326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * @param notificationStatus 180326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde */ 181fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void handleRegistration(int masId, int notificationStatus){ 182326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(D) Log.d(TAG, "handleRegistration( " + masId + ", " + notificationStatus + ")"); 183fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 184fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if(notificationStatus == BluetoothMapAppParams.NOTIFICATION_STATUS_NO) { 185326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRegisteredMasIds.delete(masId); 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 */ 190326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(isConnected() == false) { 191326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(D) Log.d(TAG, "handleRegistration: connect"); 192326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde connect(); 193fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 194326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mRegisteredMasIds.put(masId, true); // We don't use the value for anything 195326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 196326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mRegisteredMasIds.size() == 0) { 197326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde // No more registrations - disconnect 198326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(D) Log.d(TAG, "handleRegistration: disconnect"); 199326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde disconnect(); 200fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 201fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 202fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 203fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie public void connect() { 204326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 205326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mConnected = true; 206fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 207fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothSocket btSocket = null; 208fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 209bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // TODO: Do SDP record search again? 210bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(mMnsRecord != null && mMnsRecord.getL2capPsm() > 0) { 211bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // Do L2CAP connect 212bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde btSocket = mRemoteDevice.createL2capSocket(mMnsRecord.getL2capPsm()); 213bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 214bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } else if (mMnsRecord != null && mMnsRecord.getRfcommChannelNumber() > 0) { 215bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // Do Rfcomm connect 216bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde btSocket = mRemoteDevice.createRfcommSocket(mMnsRecord.getRfcommChannelNumber()); 217bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } else { 218bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // This should not happen... 219bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "Invalid SDP content - attempt a connect to UUID..."); 220bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // TODO: Why insecure? - is it because the link is already encrypted? 221bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde btSocket = mRemoteDevice.createInsecureRfcommSocketToServiceRecord( 222bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde BLUETOOTH_UUID_OBEX_MNS.getUuid()); 223bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 224fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie btSocket.connect(); 225fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 226fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "BtSocket Connect error " + e.getMessage(), e); 227fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // TODO: do we need to report error somewhere? 228326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mConnected = false; 229fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return; 230fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 231fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 232bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mTransport = new BluetoothObexTransport(btSocket); 233fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 234fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 235fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mClientSession = new ClientSession(mTransport); 236fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e1) { 237fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "OBEX session create error " + e1.getMessage()); 238326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mConnected = false; 239fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 240fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (mConnected && mClientSession != null) { 241326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde boolean connected = false; 242fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie HeaderSet hs = new HeaderSet(); 243fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // bb582b41-420c-11db-b0de-0800200c9a66 244fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie byte[] mnsTarget = { (byte) 0xbb, (byte) 0x58, (byte) 0x2b, (byte) 0x41, 245fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0x42, (byte) 0x0c, (byte) 0x11, (byte) 0xdb, 246fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0xb0, (byte) 0xde, (byte) 0x08, (byte) 0x00, 247fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie (byte) 0x20, (byte) 0x0c, (byte) 0x9a, (byte) 0x66 }; 248fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie hs.setHeader(HeaderSet.TARGET, mnsTarget); 249fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 250fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 251fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = true; 252fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 253fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 254326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mHsConnect = mClientSession.connect(hs); 255fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (D) Log.d(TAG, "OBEX session created"); 256326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde connected = true; 257fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 258fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "OBEX session connect error " + e.getMessage()); 259fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 260326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde mConnected = connected; 261fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 2625a60e47497f21f64e6d79420dc4c56c1907df22akschulz synchronized (this) { 263fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = false; 264fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 265fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 266fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 267326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde /** 268326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * Call this method to queue an event report to be send to the MNS server. 269326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * @param eventBytes the encoded event data. 270326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde * @param masInstanceId the MasId of the instance sending the event. 271326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde */ 272326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde public void sendEvent(byte[] eventBytes, int masInstanceId) { 273326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde // We need to check for null, to handle shutdown. 274326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mHandler != null) { 275326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde Message msg = mHandler.obtainMessage(MSG_MNS_SEND_EVENT, masInstanceId, 0, eventBytes); 276326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(msg != null) { 277326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde msg.sendToTarget(); 278326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 279326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 280326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde notifyUpdateWakeLock(); 281326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 282326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde 283326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde private int sendEventHandler(byte[] eventBytes, int masInstanceId) { 284fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 285fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie boolean error = false; 286fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int responseCode = -1; 287fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie HeaderSet request; 288fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie int maxChunkSize, bytesToWrite, bytesWritten = 0; 2899679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu ClientSession clientSession = mClientSession; 2909679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu 2919679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu if ((!mConnected) || (clientSession == null)) { 2929679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu Log.w(TAG, "sendEvent after disconnect:" + mConnected); 2939679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu return responseCode; 2949679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } 2959679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu 296fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request = new HeaderSet(); 297fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie BluetoothMapAppParams appParams = new BluetoothMapAppParams(); 298fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie appParams.setMasInstanceId(masInstanceId); 299fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 300fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie ClientOperation putOperation = null; 301fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie OutputStream outputStream = null; 302fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 303fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 304fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request.setHeader(HeaderSet.TYPE, TYPE_EVENT); 305fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie request.setHeader(HeaderSet.APPLICATION_PARAMETER, appParams.EncodeParams()); 306fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 307326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if (mHsConnect.mConnectionID != null) { 3089679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu request.mConnectionID = new byte[4]; 309326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde System.arraycopy(mHsConnect.mConnectionID, 0, request.mConnectionID, 0, 4); 3109679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } else { 3119679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu Log.w(TAG, "sendEvent: no connection ID"); 3129679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } 313fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 314fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 315fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = true; 316fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 317fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // Send the header first and then the body 318fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 319fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Send headerset Event "); 3209679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu putOperation = (ClientOperation)clientSession.put(request); 321fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie // TODO - Should this be kept or Removed 322fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 323fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 324fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when put HeaderSet " + e.getMessage()); 325fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 326fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 327fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie synchronized (this) { 328fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie mWaitingForRemote = false; 329fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 330fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (!error) { 331fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 332fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Send headerset Event "); 333fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie outputStream = putOperation.openOutputStream(); 334fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 335fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when opening OutputStream " + e.getMessage()); 336fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 337fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 338fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 339fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 340fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (!error) { 341fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 342fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie maxChunkSize = putOperation.getMaxPacketSize(); 343fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 344fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie while (bytesWritten < eventBytes.length) { 345fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie bytesToWrite = Math.min(maxChunkSize, eventBytes.length - bytesWritten); 346fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie outputStream.write(eventBytes, bytesWritten, bytesToWrite); 347fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie bytesWritten += bytesToWrite; 348fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 349fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 350fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (bytesWritten == eventBytes.length) { 351fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "SendEvent finished send length" + eventBytes.length); 352fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } else { 353fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 354fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie putOperation.abort(); 355fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "SendEvent interrupted"); 356fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 357fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 358fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 359fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie handleSendException(e.toString()); 360fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 361fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IndexOutOfBoundsException e) { 362fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie handleSendException(e.toString()); 363fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie error = true; 364fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } finally { 365fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie try { 3669679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu if (outputStream != null) { 3679679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu outputStream.close(); 3689679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } 3699679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } catch (IOException e) { 3709679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu Log.e(TAG, "Error when closing stream after send " + e.getMessage()); 3719679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu } 3729679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu try { 3739679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu if ((!error) && (putOperation != null)) { 374fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie responseCode = putOperation.getResponseCode(); 375fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (responseCode != -1) { 376fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (V) Log.v(TAG, "Put response code " + responseCode); 377fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (responseCode != ResponseCodes.OBEX_HTTP_OK) { 378fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.i(TAG, "Response error code is " + responseCode); 379fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 380fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 381fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 382fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie if (putOperation != null) { 3839679a425747e95082e169b3bd3673ed6b5a27590Zhihai Xu putOperation.close(); 384fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 385fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } catch (IOException e) { 386fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when closing stream after send " + e.getMessage()); 387fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 388fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 389fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 390fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie return responseCode; 391fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 392fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie 393fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie private void handleSendException(String exception) { 394fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie Log.e(TAG, "Error when sending event: " + exception); 395fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie } 396c09b531ba47eee740485b0c6022981fc38ef1587Zhihai Xu 397c09b531ba47eee740485b0c6022981fc38ef1587Zhihai Xu private void notifyUpdateWakeLock() { 398326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde if(mCallback != null) { 399326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde Message msg = Message.obtain(mCallback); 400326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde msg.what = BluetoothMapService.MSG_ACQUIRE_WAKE_LOCK; 401326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde msg.sendToTarget(); 402326b5e610063ac24c0ba467ac585bd4c7f618a67Casper Bonde } 403c09b531ba47eee740485b0c6022981fc38ef1587Zhihai Xu } 404fd6603b8bf9ed72dcc8bd59aaef3209251b6e17cMatthew Xie} 405