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