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.map;
18192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
19192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.BluetoothAdapter;
20192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.BluetoothServerSocket;
21192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.bluetooth.BluetoothSocket;
22192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.os.Handler;
23192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.os.Message;
24192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.os.ParcelUuid;
25192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.Log;
26192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport android.util.SparseArray;
27192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
28192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.IOException;
29192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.io.InterruptedIOException;
30192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport java.lang.ref.WeakReference;
31192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
32192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaimport javax.obex.ServerSession;
33192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
34192d793d2586b620027edd5b45ff4c72a86cc7beHemant Guptaclass BluetoothMnsService {
35192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
36192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static final String TAG = "BluetoothMnsService";
37192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
38192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static final ParcelUuid MAP_MNS =
39192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
40192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
41192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static final int MSG_EVENT = 1;
42192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
43192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    /* for BluetoothMasClient */
44192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static final int EVENT_REPORT = 1001;
45192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
46192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    /* these are shared across instances */
47192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static private SparseArray<Handler> mCallbacks = null;
48192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static private SocketAcceptThread mAcceptThread = null;
49192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static private Handler mSessionHandler = null;
50192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    static private BluetoothServerSocket mServerSocket = null;
51192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
52192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static class SessionHandler extends Handler {
53192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
54192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private final WeakReference<BluetoothMnsService> mService;
55192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
56192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        SessionHandler(BluetoothMnsService service) {
57192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mService = new WeakReference<BluetoothMnsService>(service);
58192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
59192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
60192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        @Override
61192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        public void handleMessage(Message msg) {
62192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.d(TAG, "Handler: msg: " + msg.what);
63192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
64192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            switch (msg.what) {
65192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                case MSG_EVENT:
66192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    int instanceId = msg.arg1;
67192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
68192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    synchronized (mCallbacks) {
69192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        Handler cb = mCallbacks.get(instanceId);
70192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
71192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        if (cb != null) {
72192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            BluetoothMapEventReport ev = (BluetoothMapEventReport) msg.obj;
73192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            cb.obtainMessage(EVENT_REPORT, ev).sendToTarget();
74192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        } else {
75192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            Log.w(TAG, "Got event for instance which is not registered: "
76192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                                    + instanceId);
77192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        }
78192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    }
79192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    break;
80192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
81192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
82192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
83192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
84192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    private static class SocketAcceptThread extends Thread {
85192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
86192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        private boolean mInterrupted = false;
87192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
88192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        @Override
89192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        public void run() {
90192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
91192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mServerSocket != null) {
92192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                Log.w(TAG, "Socket already created, exiting");
93192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                return;
94192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
95192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
96192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            try {
97192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
98192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mServerSocket = adapter.listenUsingEncryptedRfcommWithServiceRecord(
99192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        "MAP Message Notification Service", MAP_MNS.getUuid());
100192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            } catch (IOException e) {
101192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mInterrupted = true;
102192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                Log.e(TAG, "I/O exception when trying to create server socket", e);
103192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
104192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
105192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            while (!mInterrupted) {
106192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                try {
107192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    Log.v(TAG, "waiting to accept connection...");
108192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
109192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    BluetoothSocket sock = mServerSocket.accept();
110192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
111192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    Log.v(TAG, "new incoming connection from "
112192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            + sock.getRemoteDevice().getName());
113192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
114192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    // session will live until closed by remote
115192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    BluetoothMnsObexServer srv = new BluetoothMnsObexServer(mSessionHandler);
116192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(
117192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                            sock);
118192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    new ServerSession(transport, srv, null);
119192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                } catch (IOException ex) {
120192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    Log.v(TAG, "I/O exception when waiting to accept (aborted?)");
121192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mInterrupted = true;
122192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
123192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
124192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
125192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mServerSocket != null) {
126192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                try {
127192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mServerSocket.close();
128192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                } catch (IOException e) {
129192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    // do nothing
130192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
131192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
132192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mServerSocket = null;
133192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
134192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
135192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
136192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
137192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    BluetoothMnsService() {
138192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.v(TAG, "BluetoothMnsService()");
139192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
140192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mCallbacks == null) {
141192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.v(TAG, "BluetoothMnsService(): allocating callbacks");
142192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mCallbacks = new SparseArray<Handler>();
143192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
144192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
145192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        if (mSessionHandler == null) {
146192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            Log.v(TAG, "BluetoothMnsService(): allocating session handler");
147192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mSessionHandler = new SessionHandler(this);
148192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
149192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
150192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
151192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public void registerCallback(int instanceId, Handler callback) {
152192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.v(TAG, "registerCallback()");
153192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
154192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        synchronized (mCallbacks) {
155192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mCallbacks.put(instanceId, callback);
156192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
157192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mAcceptThread == null) {
158192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                Log.v(TAG, "registerCallback(): starting MNS server");
159192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mAcceptThread = new SocketAcceptThread();
160192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mAcceptThread.setName("BluetoothMnsAcceptThread");
161192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mAcceptThread.start();
162192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
163192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
164192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
165192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
166192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    public void unregisterCallback(int instanceId) {
167192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        Log.v(TAG, "unregisterCallback()");
168192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
169192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        synchronized (mCallbacks) {
170192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            mCallbacks.remove(instanceId);
171192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
172192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            if (mCallbacks.size() == 0) {
173192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                Log.v(TAG, "unregisterCallback(): shutting down MNS server");
174192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
175192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                if (mServerSocket != null) {
176192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    try {
177192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                        mServerSocket.close();
178192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    } catch (IOException e) {
179192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    }
180192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
181192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mServerSocket = null;
182192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
183192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
184192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mAcceptThread.interrupt();
185192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
186192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                try {
187192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                    mAcceptThread.join(5000);
188192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                } catch (InterruptedException e) {
189192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                }
190192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta
191192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta                mAcceptThread = null;
192192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta            }
193192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta        }
194192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta    }
195192d793d2586b620027edd5b45ff4c72a86cc7beHemant Gupta}
196