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