1192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta/*
2192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Copyright (C) 2014 The Android Open Source Project
3192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta *
4192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Licensed under the Apache License, Version 2.0 (the "License");
5192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * you may not use this file except in compliance with the License.
6192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * You may obtain a copy of the License at
7192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta *
8192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta *      http://www.apache.org/licenses/LICENSE-2.0
9192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta *
10192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * Unless required by applicable law or agreed to in writing, software
11192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * distributed under the License is distributed on an "AS IS" BASIS,
12192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * See the License for the specific language governing permissions and
14192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta * limitations under the License.
15192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta */
16192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
17192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptapackage android.bluetooth.client.pbap;
18192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
19192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.os.Handler;
20192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.Log;
21192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
22192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.IOException;
23192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
24192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ClientSession;
25192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.HeaderSet;
26192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ObexTransport;
27192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ResponseCodes;
28192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
29192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptafinal class BluetoothPbapObexSession {
30192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static final String TAG = "BluetoothPbapObexSession";
31192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
32192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static final byte[] PBAP_TARGET = new byte[] {
33192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66,
34192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66
35192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    };
36192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
37192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_CONNECTED = 100;
38192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_FAILED = 101;
39192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_DISCONNECTED = 102;
40192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_REQUEST_COMPLETED = 103;
41192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_REQUEST_FAILED = 104;
42192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_AUTHENTICATION_REQUEST = 105;
43192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    final static int OBEX_SESSION_AUTHENTICATION_TIMEOUT = 106;
44192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
45192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private Handler mSessionHandler;
46192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private final ObexTransport mTransport;
47192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private ObexClientThread mObexClientThread;
48192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private BluetoothPbapObexAuthenticator mAuth = null;
49192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
50192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public BluetoothPbapObexSession(ObexTransport transport) {
51192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mTransport = transport;
52192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
53192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
54192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public void start(Handler handler) {
55192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.d(TAG, "start");
56192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mSessionHandler = handler;
57192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
58192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mAuth = new BluetoothPbapObexAuthenticator(mSessionHandler);
59192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
60192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mObexClientThread = new ObexClientThread();
61192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mObexClientThread.start();
62192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
63192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
64192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public void stop() {
65192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.d(TAG, "stop");
66192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
67192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mObexClientThread != null) {
68192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            try {
69192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mObexClientThread.interrupt();
70192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mObexClientThread.join();
71192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mObexClientThread = null;
72192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            } catch (InterruptedException e) {
73192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
74192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
75192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
76192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
77192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public void abort() {
78192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.d(TAG, "abort");
79192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
80192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mObexClientThread != null && mObexClientThread.mRequest != null) {
81192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            /*
82192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta             * since abort may block until complete GET is processed inside OBEX
83192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta             * session, let's run it in separate thread so it won't block UI
84192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta             */
85192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            (new Thread() {
86192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                @Override
87192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                public void run() {
88192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mObexClientThread.mRequest.abort();
89192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
90192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }).run();
91192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
92192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
93192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
94192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public boolean schedule(BluetoothPbapRequest request) {
95192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.d(TAG, "schedule: " + request.getClass().getSimpleName());
96192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
97192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mObexClientThread == null) {
98192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.e(TAG, "OBEX session not started");
99192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            return false;
100192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
101192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
102192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        return mObexClientThread.schedule(request);
103192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
104192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
105192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public boolean setAuthReply(String key) {
106192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.d(TAG, "setAuthReply key=" + key);
107192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
108192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mAuth == null) {
109192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            return false;
110192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
111192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
112192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        mAuth.setReply(key);
113192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
114192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        return true;
115192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
116192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
117192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private class ObexClientThread extends Thread {
118192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
119192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private static final String TAG = "ObexClientThread";
120192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
121192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private ClientSession mClientSession;
122192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private BluetoothPbapRequest mRequest;
123192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
124192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private volatile boolean mRunning = true;
125192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
126192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        public ObexClientThread() {
127192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
128192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mClientSession = null;
129192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mRequest = null;
130192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
131192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
132192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        @Override
133192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        public void run() {
134192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            super.run();
135192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
136192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (!connect()) {
137192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mSessionHandler.obtainMessage(OBEX_SESSION_FAILED).sendToTarget();
138192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                return;
139192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
140192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
141192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mSessionHandler.obtainMessage(OBEX_SESSION_CONNECTED).sendToTarget();
142192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
143192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            while (mRunning) {
144192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                synchronized (this) {
145192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    try {
146192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        if (mRequest == null) {
147192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            this.wait();
148192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        }
149192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    } catch (InterruptedException e) {
150192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mRunning = false;
151192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        break;
152192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    }
153192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
154192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
155192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                if (mRunning && mRequest != null) {
156192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    try {
157192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mRequest.execute(mClientSession);
158192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    } catch (IOException e) {
159192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        // this will "disconnect" for cleanup
160192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mRunning = false;
161192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    }
162192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
163192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    if (mRequest.isSuccess()) {
164192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_COMPLETED, mRequest)
165192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                                .sendToTarget();
166192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    } else {
167192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mSessionHandler.obtainMessage(OBEX_SESSION_REQUEST_FAILED, mRequest)
168192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                                .sendToTarget();
169192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    }
170192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
171192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
172192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mRequest = null;
173192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
174192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
175192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            disconnect();
176192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
177192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mSessionHandler.obtainMessage(OBEX_SESSION_DISCONNECTED).sendToTarget();
178192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
179192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
180192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        public synchronized boolean schedule(BluetoothPbapRequest request) {
181192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.d(TAG, "schedule: " + request.getClass().getSimpleName());
182192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
183192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mRequest != null) {
184192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                return false;
185192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
186192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
187192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mRequest = request;
188192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            notify();
189192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
190192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            return true;
191192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
192192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
193192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private boolean connect() {
194192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.d(TAG, "connect");
195192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
196192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            try {
197192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mClientSession = new ClientSession(mTransport);
198192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mClientSession.setAuthenticator(mAuth);
199192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            } catch (IOException e) {
200192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                return false;
201192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
202192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
203192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            HeaderSet hs = new HeaderSet();
204192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            hs.setHeader(HeaderSet.TARGET, PBAP_TARGET);
205192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
206192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            try {
207192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                hs = mClientSession.connect(hs);
208192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
209192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                if (hs.getResponseCode() != ResponseCodes.OBEX_HTTP_OK) {
210192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    disconnect();
211192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    return false;
212192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
213192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            } catch (IOException e) {
214192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                return false;
215192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
216192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
217192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            return true;
218192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
219192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
220192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private void disconnect() {
221192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.d(TAG, "disconnect");
222192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
223192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mClientSession != null) {
224192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                try {
225192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mClientSession.disconnect(null);
226192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mClientSession.close();
227192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                } catch (IOException e) {
228192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
229192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
230192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
231192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
232192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta}
233