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 17package android.bluetooth.client.map; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothServerSocket; 21import android.bluetooth.BluetoothSocket; 22import android.os.Handler; 23import android.os.Message; 24import android.os.ParcelUuid; 25import android.util.Log; 26import android.util.SparseArray; 27 28import java.io.IOException; 29import java.io.InterruptedIOException; 30import java.lang.ref.WeakReference; 31 32import javax.obex.ServerSession; 33 34class BluetoothMnsService { 35 36 private static final String TAG = "BluetoothMnsService"; 37 38 private static final ParcelUuid MAP_MNS = 39 ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB"); 40 41 static final int MSG_EVENT = 1; 42 43 /* for BluetoothMasClient */ 44 static final int EVENT_REPORT = 1001; 45 46 /* these are shared across instances */ 47 static private SparseArray<Handler> mCallbacks = null; 48 static private SocketAcceptThread mAcceptThread = null; 49 static private Handler mSessionHandler = null; 50 static private BluetoothServerSocket mServerSocket = null; 51 52 private static class SessionHandler extends Handler { 53 54 private final WeakReference<BluetoothMnsService> mService; 55 56 SessionHandler(BluetoothMnsService service) { 57 mService = new WeakReference<BluetoothMnsService>(service); 58 } 59 60 @Override 61 public void handleMessage(Message msg) { 62 Log.d(TAG, "Handler: msg: " + msg.what); 63 64 switch (msg.what) { 65 case MSG_EVENT: 66 int instanceId = msg.arg1; 67 68 synchronized (mCallbacks) { 69 Handler cb = mCallbacks.get(instanceId); 70 71 if (cb != null) { 72 BluetoothMapEventReport ev = (BluetoothMapEventReport) msg.obj; 73 cb.obtainMessage(EVENT_REPORT, ev).sendToTarget(); 74 } else { 75 Log.w(TAG, "Got event for instance which is not registered: " 76 + instanceId); 77 } 78 } 79 break; 80 } 81 } 82 } 83 84 private static class SocketAcceptThread extends Thread { 85 86 private boolean mInterrupted = false; 87 88 @Override 89 public void run() { 90 91 if (mServerSocket != null) { 92 Log.w(TAG, "Socket already created, exiting"); 93 return; 94 } 95 96 try { 97 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 98 mServerSocket = adapter.listenUsingEncryptedRfcommWithServiceRecord( 99 "MAP Message Notification Service", MAP_MNS.getUuid()); 100 } catch (IOException e) { 101 mInterrupted = true; 102 Log.e(TAG, "I/O exception when trying to create server socket", e); 103 } 104 105 while (!mInterrupted) { 106 try { 107 Log.v(TAG, "waiting to accept connection..."); 108 109 BluetoothSocket sock = mServerSocket.accept(); 110 111 Log.v(TAG, "new incoming connection from " 112 + sock.getRemoteDevice().getName()); 113 114 // session will live until closed by remote 115 BluetoothMnsObexServer srv = new BluetoothMnsObexServer(mSessionHandler); 116 BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport( 117 sock); 118 new ServerSession(transport, srv, null); 119 } catch (IOException ex) { 120 Log.v(TAG, "I/O exception when waiting to accept (aborted?)"); 121 mInterrupted = true; 122 } 123 } 124 125 if (mServerSocket != null) { 126 try { 127 mServerSocket.close(); 128 } catch (IOException e) { 129 // do nothing 130 } 131 132 mServerSocket = null; 133 } 134 } 135 } 136 137 BluetoothMnsService() { 138 Log.v(TAG, "BluetoothMnsService()"); 139 140 if (mCallbacks == null) { 141 Log.v(TAG, "BluetoothMnsService(): allocating callbacks"); 142 mCallbacks = new SparseArray<Handler>(); 143 } 144 145 if (mSessionHandler == null) { 146 Log.v(TAG, "BluetoothMnsService(): allocating session handler"); 147 mSessionHandler = new SessionHandler(this); 148 } 149 } 150 151 public void registerCallback(int instanceId, Handler callback) { 152 Log.v(TAG, "registerCallback()"); 153 154 synchronized (mCallbacks) { 155 mCallbacks.put(instanceId, callback); 156 157 if (mAcceptThread == null) { 158 Log.v(TAG, "registerCallback(): starting MNS server"); 159 mAcceptThread = new SocketAcceptThread(); 160 mAcceptThread.setName("BluetoothMnsAcceptThread"); 161 mAcceptThread.start(); 162 } 163 } 164 } 165 166 public void unregisterCallback(int instanceId) { 167 Log.v(TAG, "unregisterCallback()"); 168 169 synchronized (mCallbacks) { 170 mCallbacks.remove(instanceId); 171 172 if (mCallbacks.size() == 0) { 173 Log.v(TAG, "unregisterCallback(): shutting down MNS server"); 174 175 if (mServerSocket != null) { 176 try { 177 mServerSocket.close(); 178 } catch (IOException e) { 179 } 180 181 mServerSocket = null; 182 } 183 184 mAcceptThread.interrupt(); 185 186 try { 187 mAcceptThread.join(5000); 188 } catch (InterruptedException e) { 189 } 190 191 mAcceptThread = null; 192 } 193 } 194 } 195} 196