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