1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth.client.map;
18
19import android.os.Handler;
20import android.os.HandlerThread;
21import android.os.Looper;
22import android.os.Message;
23import android.os.Process;
24import android.util.Log;
25
26import java.io.IOException;
27import java.lang.ref.WeakReference;
28
29import javax.obex.ClientSession;
30import javax.obex.HeaderSet;
31import javax.obex.ObexTransport;
32import javax.obex.ResponseCodes;
33
34class BluetoothMasObexClientSession {
35    private static final String TAG = "BluetoothMasObexClientSession";
36
37    private static final byte[] MAS_TARGET = new byte[] {
38            (byte) 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, (byte) 0xdb, (byte) 0xb0, (byte) 0xde,
39            0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66
40    };
41
42    private boolean DBG = true;
43
44    static final int MSG_OBEX_CONNECTED = 100;
45    static final int MSG_OBEX_DISCONNECTED = 101;
46    static final int MSG_REQUEST_COMPLETED = 102;
47
48    private static final int CONNECT = 0;
49    private static final int DISCONNECT = 1;
50    private static final int REQUEST = 2;
51
52    private final ObexTransport mTransport;
53
54    private final Handler mSessionHandler;
55
56    private ClientSession mSession;
57
58    private HandlerThread mThread;
59    private Handler mHandler;
60
61    private boolean mConnected;
62
63    private static class ObexClientHandler extends Handler {
64       WeakReference<BluetoothMasObexClientSession> mInst;
65
66       ObexClientHandler(Looper looper, BluetoothMasObexClientSession inst) {
67          super(looper);
68          mInst = new WeakReference<BluetoothMasObexClientSession>(inst);
69       }
70
71       @Override
72       public void handleMessage(Message msg) {
73          BluetoothMasObexClientSession inst = mInst.get();
74          if (!inst.connected() && msg.what != CONNECT) {
75              Log.w(TAG, "Cannot execute " + msg + " when not CONNECTED.");
76              return;
77          }
78
79          switch (msg.what) {
80              case CONNECT:
81                  inst.connect();
82                  break;
83
84              case DISCONNECT:
85                  inst.disconnect();
86                  break;
87
88              case REQUEST:
89                  inst.executeRequest((BluetoothMasRequest) msg.obj);
90                  break;
91          }
92       }
93    }
94
95    public BluetoothMasObexClientSession(ObexTransport transport, Handler handler) {
96        mTransport = transport;
97        mSessionHandler = handler;
98    }
99
100    public void start() {
101        if (DBG) Log.d(TAG, "start called.");
102        if (mConnected) {
103            if (DBG) Log.d(TAG, "Already connected, nothing to do.");
104            return;
105        }
106
107        // Start a thread to handle messages here.
108        mThread = new HandlerThread("BluetoothMasObexClientSessionThread");
109        mThread.start();
110        mHandler = new ObexClientHandler(mThread.getLooper(), this);
111
112        // Connect it to the target device via OBEX.
113        mHandler.obtainMessage(CONNECT).sendToTarget();
114    }
115
116    public boolean makeRequest(BluetoothMasRequest request) {
117        if (DBG) Log.d(TAG, "makeRequest called with: " + request);
118
119        boolean status = mHandler.sendMessage(mHandler.obtainMessage(REQUEST, request));
120        if (!status) {
121            Log.e(TAG, "Adding messages failed, state: " + mConnected);
122            return false;
123        }
124        return true;
125    }
126
127    public void stop() {
128        if (DBG) Log.d(TAG, "stop called...");
129
130        mThread.quit();
131        disconnect();
132    }
133
134    private void connect() {
135        try {
136            mSession = new ClientSession(mTransport);
137
138            HeaderSet headerset = new HeaderSet();
139            headerset.setHeader(HeaderSet.TARGET, MAS_TARGET);
140
141            headerset = mSession.connect(headerset);
142
143            if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK) {
144                mConnected = true;
145                mSessionHandler.obtainMessage(MSG_OBEX_CONNECTED).sendToTarget();
146            } else {
147                disconnect();
148            }
149        } catch (IOException e) {
150            disconnect();
151        }
152    }
153
154    private void disconnect() {
155        if (mSession != null) {
156            try {
157                mSession.disconnect(null);
158            } catch (IOException e) {
159            }
160
161            try {
162                mSession.close();
163            } catch (IOException e) {
164            }
165        }
166
167        mConnected = false;
168        mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget();
169    }
170
171    private void executeRequest(BluetoothMasRequest request) {
172        try {
173            request.execute(mSession);
174            mSessionHandler.obtainMessage(MSG_REQUEST_COMPLETED, request).sendToTarget();
175        } catch (IOException e) {
176            if (DBG) Log.d(TAG, "Request failed: " + request);
177
178            // Disconnect to cleanup.
179            disconnect();
180        }
181    }
182
183
184    private boolean connected() {
185        return mConnected;
186    }
187}
188