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 
17 package android.bluetooth.client.map;
18 
19 import android.os.Handler;
20 import android.os.Process;
21 import android.util.Log;
22 
23 import java.io.IOException;
24 
25 import javax.obex.ClientSession;
26 import javax.obex.HeaderSet;
27 import javax.obex.ObexTransport;
28 import javax.obex.ResponseCodes;
29 
30 class BluetoothMasObexClientSession {
31     private static final String TAG = "BluetoothMasObexClientSession";
32 
33     private static final byte[] MAS_TARGET = new byte[] {
34             (byte) 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, (byte) 0xdb, (byte) 0xb0, (byte) 0xde,
35             0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66
36     };
37 
38     static final int MSG_OBEX_CONNECTED = 100;
39     static final int MSG_OBEX_DISCONNECTED = 101;
40     static final int MSG_REQUEST_COMPLETED = 102;
41 
42     private final ObexTransport mTransport;
43 
44     private final Handler mSessionHandler;
45 
46     private ClientThread mClientThread;
47 
48     private volatile boolean mInterrupted;
49 
50     private class ClientThread extends Thread {
51         private final ObexTransport mTransport;
52 
53         private ClientSession mSession;
54 
55         private BluetoothMasRequest mRequest;
56 
57         private boolean mConnected;
58 
ClientThread(ObexTransport transport)59         public ClientThread(ObexTransport transport) {
60             super("MAS ClientThread");
61 
62             mTransport = transport;
63             mConnected = false;
64         }
65 
66         @Override
run()67         public void run() {
68             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
69 
70             connect();
71 
72             if (mConnected) {
73                 mSessionHandler.obtainMessage(MSG_OBEX_CONNECTED).sendToTarget();
74             } else {
75                 mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget();
76                 return;
77             }
78 
79             while (!mInterrupted) {
80                 synchronized (this) {
81                     if (mRequest == null) {
82                         try {
83                             this.wait();
84                         } catch (InterruptedException e) {
85                             mInterrupted = true;
86                         }
87                     }
88                 }
89 
90                 if (!mInterrupted && mRequest != null) {
91                     try {
92                         mRequest.execute(mSession);
93                     } catch (IOException e) {
94                         // this will "disconnect" to cleanup
95                         mInterrupted = true;
96                     }
97 
98                     BluetoothMasRequest oldReq = mRequest;
99                     mRequest = null;
100 
101                     mSessionHandler.obtainMessage(MSG_REQUEST_COMPLETED, oldReq).sendToTarget();
102                 }
103             }
104 
105             disconnect();
106 
107             mSessionHandler.obtainMessage(MSG_OBEX_DISCONNECTED).sendToTarget();
108         }
109 
connect()110         private void connect() {
111             try {
112                 mSession = new ClientSession(mTransport);
113 
114                 HeaderSet headerset = new HeaderSet();
115                 headerset.setHeader(HeaderSet.TARGET, MAS_TARGET);
116 
117                 headerset = mSession.connect(headerset);
118 
119                 if (headerset.getResponseCode() == ResponseCodes.OBEX_HTTP_OK) {
120                     mConnected = true;
121                 } else {
122                     disconnect();
123                 }
124             } catch (IOException e) {
125             }
126         }
127 
disconnect()128         private void disconnect() {
129             try {
130                 mSession.disconnect(null);
131             } catch (IOException e) {
132             }
133 
134             try {
135                 mSession.close();
136             } catch (IOException e) {
137             }
138 
139             mConnected = false;
140         }
141 
schedule(BluetoothMasRequest request)142         public synchronized boolean schedule(BluetoothMasRequest request) {
143             if (mRequest != null) {
144                 return false;
145             }
146 
147             mRequest = request;
148             notify();
149 
150             return true;
151         }
152     }
153 
BluetoothMasObexClientSession(ObexTransport transport, Handler handler)154     public BluetoothMasObexClientSession(ObexTransport transport, Handler handler) {
155         mTransport = transport;
156         mSessionHandler = handler;
157     }
158 
start()159     public void start() {
160         if (mClientThread == null) {
161             mClientThread = new ClientThread(mTransport);
162             mClientThread.start();
163         }
164 
165     }
166 
stop()167     public void stop() {
168         if (mClientThread != null) {
169             mClientThread.interrupt();
170 
171             (new Thread() {
172                 @Override
173                 public void run() {
174                     try {
175                         mClientThread.join();
176                         mClientThread = null;
177                     } catch (InterruptedException e) {
178                         Log.w(TAG, "Interrupted while waiting for thread to join");
179                     }
180                 }
181             }).run();
182         }
183     }
184 
makeRequest(BluetoothMasRequest request)185     public boolean makeRequest(BluetoothMasRequest request) {
186         if (mClientThread == null) {
187             return false;
188         }
189 
190         return mClientThread.schedule(request);
191     }
192 }
193