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