BluetoothMapMasInstance.java revision bbb4110b455b3aa29106d5b4f0a37e1be8e09475
1/* 2* Copyright (C) 2014 Samsung System LSI 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15package com.android.bluetooth.map; 16 17import java.io.IOException; 18 19import javax.obex.ServerSession; 20 21import com.android.bluetooth.BluetoothObexTransport; 22import com.android.bluetooth.IObexConnectionHandler; 23import com.android.bluetooth.ObexServerSockets; 24import com.android.bluetooth.sdp.SdpManager; 25 26import android.bluetooth.BluetoothAdapter; 27import android.bluetooth.BluetoothDevice; 28import android.bluetooth.BluetoothServerSocket; 29import android.bluetooth.BluetoothSocket; 30import android.bluetooth.BluetoothUuid; 31import android.content.Context; 32import android.content.Intent; 33import android.os.Handler; 34import android.os.RemoteException; 35import android.util.Log; 36 37public class BluetoothMapMasInstance implements IObexConnectionHandler { 38 private static final String TAG = "BluetoothMapMasInstance"; 39 40 private static final boolean D = BluetoothMapService.DEBUG; 41 private static final boolean V = BluetoothMapService.VERBOSE; 42 43 private static final int SDP_MAP_MSG_TYPE_EMAIL = 0x01; 44 private static final int SDP_MAP_MSG_TYPE_SMS_GSM = 0x02; 45 private static final int SDP_MAP_MSG_TYPE_SMS_CDMA = 0x04; 46 private static final int SDP_MAP_MSG_TYPE_MMS = 0x08; 47 48 private static final int SDP_MAP_MAS_VERSION = 0x0102; 49 50 /* TODO: Should these be adaptive for each MAS? - e.g. read from app? */ 51 private static final int SDP_MAP_MAS_FEATURES = 0x0000007F; 52 53 private ServerSession mServerSession = null; 54 55 // The handle to the socket registration with SDP 56 private ObexServerSockets mServerSockets = null; 57 private int mSdpHandle = -1; 58 59 // The actual incoming connection handle 60 private BluetoothSocket mConnSocket = null; 61 62 private BluetoothDevice mRemoteDevice = null; // The remote connected device 63 64 private BluetoothAdapter mAdapter; 65 66 private volatile boolean mInterrupted; // Used to interrupt socket accept thread 67 68 private Handler mServiceHandler = null; // MAP service message handler 69 private BluetoothMapService mMapService = null; // Handle to the outer MAP service 70 private Context mContext = null; // MAP service context 71 private BluetoothMnsObexClient mMnsClient = null; // Shared MAP MNS client 72 private BluetoothMapEmailSettingsItem mAccount = null; // 73 private String mBaseEmailUri = null; // Email client base URI for this instance 74 private int mMasInstanceId = -1; 75 private boolean mEnableSmsMms = false; 76 BluetoothMapContentObserver mObserver; 77 78 private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK; 79 80 public static final String TYPE_SMS_MMS_STR = "SMS/MMS"; 81 public static final String TYPE_EMAIL_STR = "EMAIL"; 82 public static final String TYPE_IM_STR = "IM"; 83 84 /** 85 * Create a e-mail MAS instance 86 * @param callback 87 * @param context 88 * @param mns 89 * @param emailBaseUri - use null to create a SMS/MMS MAS instance 90 */ 91 public BluetoothMapMasInstance (BluetoothMapService mapService, 92 Context context, 93 BluetoothMapEmailSettingsItem account, 94 int masId, 95 boolean enableSmsMms) { 96 mMapService = mapService; 97 mServiceHandler = mapService.getHandler(); 98 mContext = context; 99 mAccount = account; 100 if(account != null) { 101 mBaseEmailUri = account.mBase_uri; 102 } 103 mMasInstanceId = masId; 104 mEnableSmsMms = enableSmsMms; 105 init(); 106 } 107 108 @Override 109 public String toString() { 110 return "MasId: " + mMasInstanceId + " Uri:" + mBaseEmailUri + " SMS/MMS:" + mEnableSmsMms; 111 } 112 113 private void init() { 114 mAdapter = BluetoothAdapter.getDefaultAdapter(); 115 } 116 117 public int getMasId() { 118 return mMasInstanceId; 119 } 120 121 public void startRfcommSocketListener() { 122 if (D) Log.d(TAG, "Map Service startRfcommSocketListener"); 123 124 if (mServerSession != null) { 125 if (D) Log.d(TAG, "mServerSession exists - shutting it down..."); 126 mServerSession.close(); 127 mServerSession = null; 128 } 129 if (mObserver != null) { 130 if (D) Log.d(TAG, "mObserver exists - shutting it down..."); 131 mObserver.deinit(); 132 mObserver = null; 133 } 134 135 closeConnectionSocket(); 136 137 if(mServerSockets != null) { 138 mServerSockets.prepareForNewConnect(); 139 } else { 140 141 mServerSockets = ObexServerSockets.create(this); 142 143 if(mServerSockets == null) { 144 // TODO: Handle - was not handled before 145 Log.e(TAG, "Failed to start the listeners"); 146 return; 147 } 148 if(mSdpHandle > 0) { 149 SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle); 150 } 151 mSdpHandle = createMasSdpRecord(mServerSockets.getRfcommChannel(), 152 mServerSockets.getL2capPsm()); 153 } 154 } 155 156 /** 157 * Create the MAS SDP record with the information stored in the instance. 158 * @param rfcommChannel the rfcomm channel ID 159 * @param l2capPsm the l2capPsm - set to -1 to exclude 160 */ 161 private int createMasSdpRecord(int rfcommChannel, int l2capPsm) { 162 String masName = ""; 163 int messageTypeFlags = 0; 164 if(mEnableSmsMms) { 165 masName = TYPE_SMS_MMS_STR; 166 messageTypeFlags |= SDP_MAP_MSG_TYPE_SMS_GSM | 167// SDP_MAP_MSG_TYPE_SMS_CDMA| 168 SDP_MAP_MSG_TYPE_MMS; 169 } 170 171 if(mBaseEmailUri != null) { 172 if(mEnableSmsMms) { 173 masName += "/" + TYPE_EMAIL_STR; 174 } else { 175 masName = mAccount.getName(); 176 } 177 messageTypeFlags |= SDP_MAP_MSG_TYPE_EMAIL; 178 } 179 180 return SdpManager.getDefaultManager().createMapMasRecord(masName, 181 mMasInstanceId, 182 rfcommChannel, 183 l2capPsm, 184 SDP_MAP_MAS_VERSION, 185 messageTypeFlags, 186 SDP_MAP_MAS_FEATURES); 187 } 188 189 /* Called for all MAS instances for each instance when auth. is completed, hence 190 * must check if it has a valid connection before creating a session. 191 * Returns true at success. */ 192 public boolean startObexServerSession(BluetoothMnsObexClient mnsClient) 193 throws IOException, RemoteException { 194 if (D) Log.d(TAG, "Map Service startObexServerSession masid = " + mMasInstanceId); 195 196 if (mConnSocket != null) { 197 if(mServerSession != null) { 198 // Already connected, just return true 199 return true; 200 } 201 202 mMnsClient = mnsClient; 203 BluetoothMapObexServer mapServer; 204 mObserver = new BluetoothMapContentObserver(mContext, 205 mMnsClient, 206 this, 207 mAccount, 208 mEnableSmsMms); 209 mObserver.init(); 210 mapServer = new BluetoothMapObexServer(mServiceHandler, 211 mContext, 212 mObserver, 213 mMasInstanceId, 214 mAccount, 215 mEnableSmsMms); 216 217 // setup transport 218 BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket); 219 mServerSession = new ServerSession(transport, mapServer, null); 220 if (D) Log.d(TAG, " ServerSession started."); 221 222 return true; 223 } 224 if (D) Log.d(TAG, " No connection for this instance"); 225 return false; 226 } 227 228 public boolean handleSmsSendIntent(Context context, Intent intent){ 229 if(mObserver != null) { 230 return mObserver.handleSmsSendIntent(context, intent); 231 } 232 return false; 233 } 234 235 /** 236 * Check if this instance is started. 237 * @return true if started 238 */ 239 public boolean isStarted() { 240 return (mConnSocket != null); 241 } 242 243 public void shutdown() { 244 if (D) Log.d(TAG, "MAP Service shutdown"); 245 246 if (mServerSession != null) { 247 mServerSession.close(); 248 mServerSession = null; 249 } 250 if (mObserver != null) { 251 mObserver.deinit(); 252 mObserver = null; 253 } 254 255 closeConnectionSocket(); 256 257 closeServerSockets(true); 258 } 259 260 /** 261 * Signal to the ServerSockets handler that a new connection may be accepted. 262 */ 263 public void restartObexServerSession() { 264 if (D) Log.d(TAG, "MAP Service restartObexServerSession()"); 265 startRfcommSocketListener(); 266 } 267 268 269 private final synchronized void closeServerSockets(boolean block) { 270 // exit SocketAcceptThread early 271 ObexServerSockets sockets = mServerSockets; 272 if (sockets != null) { 273 sockets.shutdown(block); 274 mServerSockets = null; 275 } 276 } 277 278 private final synchronized void closeConnectionSocket() { 279 if (mConnSocket != null) { 280 try { 281 mConnSocket.close(); 282 } catch (IOException e) { 283 Log.e(TAG, "Close Connection Socket error: ", e); 284 } finally { 285 mConnSocket = null; 286 } 287 } 288 } 289 290 public void setRemoteFeatureMask(int supported_features) { 291 mRemoteFeatureMask = supported_features; 292 } 293 294 public int getRemoteFeatureMask(){ 295 return this.mRemoteFeatureMask; 296 } 297 298 @Override 299 public synchronized boolean onConnect(BluetoothDevice device, BluetoothSocket socket) { 300 /* Signal to the service that we have received an incoming connection. 301 */ 302 boolean isValid = mMapService.onConnect(device, BluetoothMapMasInstance.this); 303 304 if(isValid == true) { 305 mRemoteDevice = device; 306 mConnSocket = socket; 307 } 308 309 return isValid; 310 } 311 312 /** 313 * Called when an unrecoverable error occurred in an accept thread. 314 * Close down the server socket, and restart. 315 * TODO: Change to message, to call start in correct context. 316 */ 317 @Override 318 public synchronized void onAcceptFailed() { 319 mServerSockets = null; // Will cause a new to be created when calling start. 320 Log.e(TAG,"Failed to accept incomming connection - restarting"); 321 startRfcommSocketListener(); 322 323 } 324 325} 326