• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.pbap;
18 
19 import android.os.Handler;
20 import android.util.Log;
21 
22 import java.io.IOException;
23 
24 import javax.obex.ClientSession;
25 import javax.obex.HeaderSet;
26 import javax.obex.ObexTransport;
27 import javax.obex.ResponseCodes;
28 
29 final class BluetoothPbapObexSession {
30     private static final String TAG = "BluetoothPbapObexSession";
31 
32     private static final byte[] PBAP_TARGET = new byte[] {
33             0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66,
34             0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66
35     };
36 
37     final static int OBEX_SESSION_CONNECTED = 100;
38     final static int OBEX_SESSION_FAILED = 101;
39     final static int OBEX_SESSION_DISCONNECTED = 102;
40     final static int OBEX_SESSION_REQUEST_COMPLETED = 103;
41     final static int OBEX_SESSION_REQUEST_FAILED = 104;
42     final static int OBEX_SESSION_AUTHENTICATION_REQUEST = 105;
43     final static int OBEX_SESSION_AUTHENTICATION_TIMEOUT = 106;
44 
45     private Handler mSessionHandler;
46     private final ObexTransport mTransport;
47     private ObexClientThread mObexClientThread;
48     private BluetoothPbapObexAuthenticator mAuth = null;
49 
BluetoothPbapObexSession(ObexTransport transport)50     public BluetoothPbapObexSession(ObexTransport transport) {
51         mTransport = transport;
52     }
53 
start(Handler handler)54     public void start(Handler handler) {
55         Log.d(TAG, "start");
56         mSessionHandler = handler;
57 
58         mAuth = new BluetoothPbapObexAuthenticator(mSessionHandler);
59 
60         mObexClientThread = new ObexClientThread();
61         mObexClientThread.start();
62     }
63 
stop()64     public void stop() {
65         Log.d(TAG, "stop");
66 
67         if (mObexClientThread != null) {
68             try {
69                 mObexClientThread.interrupt();
70                 mObexClientThread.join();
71                 mObexClientThread = null;
72             } catch (InterruptedException e) {
73             }
74         }
75     }
76 
abort()77     public void abort() {
78         Log.d(TAG, "abort");
79 
80         if (mObexClientThread != null && mObexClientThread.mRequest != null) {
81             /*
82              * since abort may block until complete GET is processed inside OBEX
83              * session, let's run it in separate thread so it won't block UI
84              */
85             (new Thread() {
86                 @Override
87                 public void run() {
88                     mObexClientThread.mRequest.abort();
89                 }
90             }).run();
91         }
92     }
93 
schedule(BluetoothPbapRequest request)94     public boolean schedule(BluetoothPbapRequest request) {
95         Log.d(TAG, "schedule: " + request.getClass().getSimpleName());
96 
97         if (mObexClientThread == null) {
98             Log.e(TAG, "OBEX session not started");
99             return false;
100         }
101 
102         return mObexClientThread.schedule(request);
103     }
104 
setAuthReply(String key)105     public boolean setAuthReply(String key) {
106         Log.d(TAG, "setAuthReply key=" + key);
107 
108         if (mAuth == null) {
109             return false;
110         }
111 
112         mAuth.setReply(key);
113 
114         return true;
115     }
116 
117     private class ObexClientThread extends Thread {
118 
119         private static final String TAG = "ObexClientThread";
120 
121         private ClientSession mClientSession;
122         private BluetoothPbapRequest mRequest;
123 
124         private volatile boolean mRunning = true;
125 
ObexClientThread()126         public ObexClientThread() {
127 
128             mClientSession = null;
129             mRequest = null;
130         }
131 
132         @Override
run()133         public void run() {
134             super.run();
135 
136             if (!connect()) {
137                 mSessionHandler.obtainMessage(OBEX_SESSION_FAILED).sendToTarget();
138                 return;
139             }
140 
141             mSessionHandler.obtainMessage(OBEX_SESSION_CONNECTED).sendToTarget();
142 
143             while (mRunning) {
144                 synchronized (this) {
145                     try {
146                         if (mRequest == null) {
147                             this.wait();
148                         }
149                     } catch (InterruptedException e) {
150                         mRunning = false;
151                         break;
152                     }
153                 }
154 
155                 if (mRunning && mRequest != null) {
156                     try {
157                         mRequest.execute(mClientSession);
158                     } catch (IOException e) {
159                         // this will "disconnect" for cleanup
160                         mRunning = false;
161                     }
162 
163                     if (mRequest.isSuccess()) {
164                         mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_COMPLETED, mRequest)
165                                 .sendToTarget();
166                     } else {
167                         mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_FAILED, mRequest)
168                                 .sendToTarget();
169                     }
170                 }
171 
172                 mRequest = null;
173             }
174 
175             disconnect();
176 
177             mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget();
178         }
179 
schedule(BluetoothPbapRequest request)180         public synchronized boolean schedule(BluetoothPbapRequest request) {
181             Log.d(TAG, "schedule: " + request.getClass().getSimpleName());
182 
183             if (mRequest != null) {
184                 return false;
185             }
186 
187             mRequest = request;
188             notify();
189 
190             return true;
191         }
192 
connect()193         private boolean connect() {
194             Log.d(TAG, "connect");
195 
196             try {
197                 mClientSession = new ClientSession(mTransport);
198                 mClientSession.setAuthenticator(mAuth);
199             } catch (IOException e) {
200                 return false;
201             }
202 
203             HeaderSet hs = new HeaderSet();
204             hs.setHeader(HeaderSet.TARGET, PBAP_TARGET);
205 
206             try {
207                 hs = mClientSession.connect(hs);
208 
209                 if (hs.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
210                     disconnect();
211                     return false;
212                 }
213             } catch (IOException e) {
214                 return false;
215             }
216 
217             return true;
218         }
219 
disconnect()220         private void disconnect() {
221             Log.d(TAG, "disconnect");
222 
223             if (mClientSession != null) {
224                 try {
225                     mClientSession.disconnect(null);
226                     mClientSession.close();
227                 } catch (IOException e) {
228                 }
229             }
230         }
231     }
232 }
233