diff options
author | Matthew Xie <mattx@google.com> | 2013-07-18 18:18:36 -0700 |
---|---|---|
committer | Zhihai Xu <zhihaixu@google.com> | 2013-08-09 18:44:53 -0700 |
commit | fd6603b8bf9ed72dcc8bd59aaef3209251b6e17c (patch) | |
tree | fecaf3c95adce97dc5176cc341d903fe488d5edf /src/com/android/bluetooth/map/BluetoothMapService.java | |
parent | bb1ac417208c8e283f9b5b49f4413856500ed0f9 (diff) | |
download | android_packages_apps_Bluetooth-fd6603b8bf9ed72dcc8bd59aaef3209251b6e17c.tar.gz android_packages_apps_Bluetooth-fd6603b8bf9ed72dcc8bd59aaef3209251b6e17c.tar.bz2 android_packages_apps_Bluetooth-fd6603b8bf9ed72dcc8bd59aaef3209251b6e17c.zip |
Bluetooth MAP profile - sms and mms support initial check-in
bug:10116530
Change-Id: If9ce878d71c1e1b12416014c433da03b3033e158
Diffstat (limited to 'src/com/android/bluetooth/map/BluetoothMapService.java')
-rw-r--r-- | src/com/android/bluetooth/map/BluetoothMapService.java | 778 |
1 files changed, 778 insertions, 0 deletions
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java new file mode 100644 index 000000000..e4da10f11 --- /dev/null +++ b/src/com/android/bluetooth/map/BluetoothMapService.java @@ -0,0 +1,778 @@ +/* +* Copyright (C) 2013 Samsung System LSI +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.android.bluetooth.map; + +import java.io.IOException; + +import javax.obex.ServerSession; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothServerSocket; +import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothMap; +import android.bluetooth.BluetoothUuid; +import android.bluetooth.BluetoothMap; +import android.bluetooth.BluetoothSocket; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.bluetooth.R; +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.AdapterService; + + +public class BluetoothMapService extends Service { + private static final String TAG = "BluetoothMapService"; + + /** + * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and + * restart com.android.bluetooth process. only enable DEBUG log: + * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and + * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE" + */ + + public static final boolean DEBUG = true; + + public static final boolean VERBOSE = true; + + /** + * Intent indicating incoming obex authentication request which is from + * PCE(Carkit) + */ + public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.map.authchall"; + + /** + * Intent indicating obex session key input complete by user which is sent + * from BluetoothMapActivity + */ + public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.map.authresponse"; + + /** + * Intent indicating user canceled obex authentication session key input + * which is sent from BluetoothMapActivity + */ + public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.map.authcancelled"; + + /** + * Intent indicating timeout for user confirmation, which is sent to + * BluetoothMapActivity + */ + public static final String USER_CONFIRM_TIMEOUT_ACTION = + "com.android.bluetooth.map.userconfirmtimeout"; + + /** + * Intent Extra name indicating session key which is sent from + * BluetoothMapActivity + */ + public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.map.sessionkey"; + + public static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; + + public static final int MSG_SERVERSESSION_CLOSE = 5000; + + public static final int MSG_SESSION_ESTABLISHED = 5001; + + public static final int MSG_SESSION_DISCONNECTED = 5002; + + public static final int MSG_OBEX_AUTH_CHALL = 5003; + + private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; + + private static final int START_LISTENER = 1; + + private static final int USER_TIMEOUT = 2; + + private static final int AUTH_TIMEOUT = 3; + + + private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; + + + // Ensure not conflict with Opp notification ID + private static final int NOTIFICATION_ID_ACCESS = -1000001; + + private static final int NOTIFICATION_ID_AUTH = -1000002; + + private PowerManager.WakeLock mWakeLock = null; + + private BluetoothAdapter mAdapter; + + private SocketAcceptThread mAcceptThread = null; + + private BluetoothMapAuthenticator mAuth = null; + + private BluetoothMapObexServer mMapServer; + + private ServerSession mServerSession = null; + + private BluetoothMnsObexClient mBluetoothMnsObexClient = null; + + private BluetoothServerSocket mServerSocket = null; + + private BluetoothSocket mConnSocket = null; + + private BluetoothDevice mRemoteDevice = null; + + private static String sLocalPhoneNum = null; + + private static String sLocalPhoneName = null; + + private static String sRemoteDeviceName = null; + + private boolean mHasStarted = false; + + private volatile boolean mInterrupted; + + private int mState; + + private int mStartId = -1; + + //private IBluetooth mBluetoothService; + + private boolean isWaitingAuthorization = false; + + // package and class name to which we send intent to check phone book access permission + private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; + private static final String ACCESS_AUTHORITY_CLASS = + "com.android.settings.bluetooth.BluetoothPermissionRequest"; + + public BluetoothMapService() { + mState = BluetoothMap.STATE_DISCONNECTED; + } + + @Override + public void onCreate() { + super.onCreate(); + if (VERBOSE) Log.v(TAG, "Map Service onCreate"); + + mInterrupted = false; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + if (!mHasStarted) { + mHasStarted = true; + if (VERBOSE) Log.v(TAG, "Starting MAP service"); + + int state = mAdapter.getState(); + if (state == BluetoothAdapter.STATE_ON) { + // start RFCOMM listener + mSessionStatusHandler.sendMessage(mSessionStatusHandler + .obtainMessage(START_LISTENER)); + } + } + } + // incoming Start intent handler + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + mStartId = startId; + if (mAdapter == null) { + Log.w(TAG, "Stopping BluetoothMapService: " + + "device does not have BT or device is not ready"); + // Release all resources + closeService(); + } else { + // No need to handle the null intent case, because we have + // all restart work done in onCreate() + if (intent != null) { + parseIntent(intent); + } + } + return START_NOT_STICKY; + } + + // process the intent from receiver + private void parseIntent(final Intent intent) { + String action = intent.getStringExtra("action"); + if (VERBOSE) Log.v(TAG, "action: " + action); + + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + if (VERBOSE) Log.v(TAG, "state: " + state); + + boolean removeTimeoutMsg = true; + // BT status have been changed check new state + if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + if (state == BluetoothAdapter.STATE_TURNING_OFF) { + // Send any pending timeout now, as this service will be destroyed. + if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) { + Intent timeoutIntent = + new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); + timeoutIntent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); + sendBroadcast(timeoutIntent, BLUETOOTH_ADMIN_PERM); + } + // Release all resources + closeService(); + } else { + removeTimeoutMsg = false; + } + // Authorization answer intent + } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { + if (!isWaitingAuthorization) { + // this reply is not for us + return; + } + + isWaitingAuthorization = false; + + if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, + BluetoothDevice.CONNECTION_ACCESS_NO) == + BluetoothDevice.CONNECTION_ACCESS_YES) { + //bluetooth connection accepted by user + if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { + boolean result = mRemoteDevice.setTrust(true); + if (VERBOSE) Log.v(TAG, "setTrust() result=" + result); + } + try { + if (mConnSocket != null) { + // start obex server and rfcomm connection + startObexServerSession(); + } else { + stopObexServerSession(); + } + } catch (IOException ex) { + Log.e(TAG, "Caught the error: " + ex.toString()); + } + } else { + stopObexServerSession(); + } + } else if (action.equals(AUTH_RESPONSE_ACTION)) { + String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); + //send auth request + notifyAuthKeyInput(sessionkey); + } else if (action.equals(AUTH_CANCELLED_ACTION)) { + //user cancelled auth request + notifyAuthCancelled(); + } else { + removeTimeoutMsg = false; + } + + if (removeTimeoutMsg) { + mSessionStatusHandler.removeMessages(USER_TIMEOUT); + } + } + + @Override + public void onDestroy() { + if (VERBOSE) Log.v(TAG, "Map Service onDestroy"); + + super.onDestroy(); + setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + closeService(); + if(mSessionStatusHandler != null) { + mSessionStatusHandler.removeCallbacksAndMessages(null); + } + } + + @Override + public IBinder onBind(Intent intent) { + if (VERBOSE) Log.v(TAG, "Map Service onBind"); + return mBinder; + } + + private void startRfcommSocketListener() { + if (VERBOSE) Log.v(TAG, "Map Service startRfcommSocketListener"); + + if (mAcceptThread == null) { + mAcceptThread = new SocketAcceptThread(); + mAcceptThread.setName("BluetoothMapAcceptThread"); + mAcceptThread.start(); + } + } + + private final boolean initSocket() { + if (VERBOSE) Log.v(TAG, "Map Service initSocket"); + + boolean initSocketOK = true; + final int CREATE_RETRY_TIME = 10; + + // It's possible that create will fail in some cases. retry for 10 times + for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { + try { + // It is mandatory for PSE to support initiation of bonding and + // encryption. + mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord + ("OBEX Message Access Server", BluetoothUuid.MAP.getUuid()); + + } catch (IOException e) { + Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); + initSocketOK = false; + } + if (!initSocketOK) { + // Need to break out of this loop if BT is being turned off. + if (mAdapter == null) break; + int state = mAdapter.getState(); + if ((state != BluetoothAdapter.STATE_TURNING_ON) && + (state != BluetoothAdapter.STATE_ON)) { + Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); + break; + } + synchronized (this) { + try { + if (VERBOSE) Log.v(TAG, "wait 300 ms"); + Thread.sleep(300); + } catch (InterruptedException e) { + Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); + mInterrupted = true; + } + } + } else { + break; + } + } + + if (initSocketOK) { + if (VERBOSE) Log.v(TAG, "Succeed to create listening socket "); + + } else { + Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); + } + return initSocketOK; + } + + private final void closeSocket(boolean server, boolean accept) throws IOException { + if (server == true) { + // Stop the possible trying to init serverSocket + mInterrupted = true; + + if (mServerSocket != null) { + mServerSocket.close(); + mServerSocket = null; + } + } + + if (accept == true) { + if (mConnSocket != null) { + mConnSocket.close(); + mConnSocket = null; + } + } + } + + private final void closeService() { + if (VERBOSE) Log.v(TAG, "Map Service closeService in"); + + try { + closeSocket(true, true); + } catch (IOException ex) { + Log.e(TAG, "CloseSocket error: " + ex); + } + + if (mAcceptThread != null) { + try { + mAcceptThread.shutdown(); + mAcceptThread.join(); + mAcceptThread = null; + } catch (InterruptedException ex) { + Log.w(TAG, "mAcceptThread close error", ex); + } + } + if (mServerSession != null) { + mServerSession.close(); + mServerSession = null; + } + if (mBluetoothMnsObexClient != null) { + try { + mBluetoothMnsObexClient.interrupt(); + mBluetoothMnsObexClient.join(); + mBluetoothMnsObexClient = null; + } catch (InterruptedException ex) { + Log.w(TAG, "mBluetoothMnsObexClient close error", ex); + } + } +// mBluetoothMnsObexClient.shutdown + + mHasStarted = false; + if (mStartId != -1 && stopSelfResult(mStartId)) { + if (VERBOSE) Log.v(TAG, "successfully stopped map service"); + mStartId = -1; + } + if (VERBOSE) Log.v(TAG, "Map Service closeService out"); + } + + private final void startObexServerSession() throws IOException { + if (VERBOSE) Log.v(TAG, "Map Service startObexServerSession"); + + // acquire the wakeLock before start Obex transaction thread + if (mWakeLock == null) { + PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "StartingObexMapTransaction"); + mWakeLock.setReferenceCounted(false); + mWakeLock.acquire(); + } + + + mMapServer = new BluetoothMapObexServer(mSessionStatusHandler, this); + synchronized (this) { + // We need to get authentication now that obex server is up + mAuth = new BluetoothMapAuthenticator(mSessionStatusHandler); + mAuth.setChallenged(false); + mAuth.setCancelled(false); + } + // setup RFCOMM transport + BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(mConnSocket); + mServerSession = new ServerSession(transport, mMapServer, mAuth); + mBluetoothMnsObexClient = new BluetoothMnsObexClient(this, mRemoteDevice); + mBluetoothMnsObexClient.start(); // Initiate the MNS message loop. + setState(BluetoothMap.STATE_CONNECTED); + if (VERBOSE) { + Log.v(TAG, "startObexServerSession() success!"); + } + } + + private void stopObexServerSession() { + if (VERBOSE) Log.v(TAG, "Map Service stopObexServerSession"); + + // Release the wake lock if obex transaction is over + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; + } + + if (mServerSession != null) { + mServerSession.close(); + mServerSession = null; + } + + mAcceptThread = null; + + if(mBluetoothMnsObexClient != null) { + mBluetoothMnsObexClient.disconnect(); + mBluetoothMnsObexClient = null; + } + + try { + closeSocket(false, true); + mConnSocket = null; + } catch (IOException e) { + Log.e(TAG, "closeSocket error: " + e.toString()); + } + // Last obex transaction is finished, we start to listen for incoming + // connection again + if (mAdapter.isEnabled()) { + startRfcommSocketListener(); + } + setState(BluetoothMap.STATE_DISCONNECTED); + } + + private void notifyAuthKeyInput(final String key) { + synchronized (mAuth) { + if (key != null) { + mAuth.setSessionKey(key); + } + mAuth.setChallenged(true); + mAuth.notify(); + } + } + + private void notifyAuthCancelled() { + synchronized (mAuth) { + mAuth.setCancelled(true); + mAuth.notify(); + } + } + + /** + * A thread that runs in the background waiting for remote rfcomm + * connect.Once a remote socket connected, this thread shall be + * shutdown.When the remote disconnect,this thread shall run again waiting + * for next request. + */ + private class SocketAcceptThread extends Thread { + + private boolean stopped = false; + + @Override + public void run() { + if (mServerSocket == null) { + if (!initSocket()) { + closeService(); + return; + } + } + + while (!stopped) { + try { + if (VERBOSE) Log.v(TAG, "Accepting socket connection..."); + mConnSocket = mServerSocket.accept(); + if (VERBOSE) Log.v(TAG, "Accepted socket connection..."); + + mRemoteDevice = mConnSocket.getRemoteDevice(); + if (mRemoteDevice == null) { + Log.i(TAG, "getRemoteDevice() = null"); + break; + } + sRemoteDeviceName = mRemoteDevice.getName(); + // In case getRemoteName failed and return null + if (TextUtils.isEmpty(sRemoteDeviceName)) { + sRemoteDeviceName = getString(R.string.defaultname); + } + boolean trust = mRemoteDevice.getTrustState(); + if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust); + + if (trust) { + try { + if (VERBOSE) Log.v(TAG, "incoming connection accepted from: " + + sRemoteDeviceName + " automatically as trusted device"); + startObexServerSession(); + } catch (IOException ex) { + Log.e(TAG, "catch exception starting obex server session" + + ex.toString()); + } + } else { + Intent intent = new + Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); + intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); + intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, + BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); + intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); + intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, + BluetoothMapReceiver.class.getName()); + sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + isWaitingAuthorization = true; + + if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: " + + sRemoteDeviceName); + + } + stopped = true; // job done ,close this thread; + } catch (IOException ex) { + stopped=true; + if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); + } + } + } + + void shutdown() { + stopped = true; + interrupt(); + } + } + + private final Handler mSessionStatusHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); + + switch (msg.what) { + case START_LISTENER: + if (mAdapter.isEnabled()) { + startRfcommSocketListener(); + } else { + closeService();// release all resources + } + break; + case USER_TIMEOUT: + + Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); + intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); + sendBroadcast(intent); + isWaitingAuthorization = false; + stopObexServerSession(); + break; + case AUTH_TIMEOUT: + Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); + sendBroadcast(i); + removeMapNotification(NOTIFICATION_ID_AUTH); + notifyAuthCancelled(); + break; + case MSG_SERVERSESSION_CLOSE: + stopObexServerSession(); + break; + case MSG_SESSION_ESTABLISHED: + break; + case MSG_SESSION_DISCONNECTED: + // handled elsewhere + break; + case MSG_OBEX_AUTH_CHALL: + createMapNotification(AUTH_CHALL_ACTION); + mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler + .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); + break; + default: + break; + } + } + }; + + private void setState(int state) { + setState(state, BluetoothMap.RESULT_SUCCESS); + } + + private synchronized void setState(int state, int result) { + if (state != mState) { + if (DEBUG) Log.d(TAG, "Map state " + mState + " -> " + state + ", result = " + + result); + int prevState = mState; + mState = state; + Intent intent = new Intent(BluetoothMap.MAP_STATE_CHANGED_ACTION); + intent.putExtra(BluetoothMap.MAP_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothMap.MAP_STATE, mState); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); + sendBroadcast(intent, BLUETOOTH_PERM); + AdapterService s = AdapterService.getAdapterService(); + if (s != null) { + s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.MAP, + mState, prevState); + } + } + } + + private void createMapNotification(String action) { + + NotificationManager nm = (NotificationManager) + getSystemService(Context.NOTIFICATION_SERVICE); + + // Create an intent triggered by clicking on the status icon. + Intent clickIntent = new Intent(); + clickIntent.setClass(this, BluetoothMapActivity.class); + clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + clickIntent.setAction(action); + + // Create an intent triggered by clicking on the + // "Clear All Notifications" button + Intent deleteIntent = new Intent(); + deleteIntent.setClass(this, BluetoothMapReceiver.class); + + Notification notification = null; + String name = getRemoteDeviceName(); + + if (action.equals(AUTH_CHALL_ACTION)) { + deleteIntent.setAction(AUTH_CANCELLED_ACTION); + notification = new Notification.Builder(this) + .setContentTitle(getString(R.string.auth_notif_title)) + .setContentText(getString(R.string.auth_notif_message,name)) + .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) + .build(); + + notification.flags |= Notification.FLAG_AUTO_CANCEL; + notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; + notification.defaults = Notification.DEFAULT_SOUND; + notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0); + nm.notify(NOTIFICATION_ID_AUTH, notification); + } + } + + private void removeMapNotification(int id) { + NotificationManager nm = (NotificationManager) + getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(id); + } + + public static String getRemoteDeviceName() { + return sRemoteDeviceName; + } + + /** + * Handlers for incoming service calls + */ + private final IBluetoothMap.Stub mBinder = new IBluetoothMap.Stub() { + public int getState() { + if (DEBUG) Log.d(TAG, "getState " + mState); + + if (!Utils.checkCaller()) { + Log.w(TAG,"getState(): not allowed for non-active user"); + return BluetoothMap.STATE_DISCONNECTED; + } + + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mState; + } + + public BluetoothDevice getClient() { + if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice); + + if (!Utils.checkCaller()) { + Log.w(TAG,"getClient(): not allowed for non-active user"); + return null; + } + + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (mState == BluetoothMap.STATE_DISCONNECTED) { + return null; + } + return mRemoteDevice; + } + + public boolean isConnected(BluetoothDevice device) { + if (!Utils.checkCaller()) { + Log.w(TAG,"isConnected(): not allowed for non-active user"); + return false; + } + + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mState == BluetoothMap.STATE_CONNECTED && mRemoteDevice.equals(device); + } + + public boolean connect(BluetoothDevice device) { + if (!Utils.checkCaller()) { + Log.w(TAG,"connect(): not allowed for non-active user"); + return false; + } + + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return false; + } + + public void disconnect() { + if (DEBUG) Log.d(TAG, "disconnect"); + + if (!Utils.checkCaller()) { + Log.w(TAG,"disconnect(): not allowed for non-active user"); + return; + } + + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + synchronized (BluetoothMapService.this) { + switch (mState) { + case BluetoothMap.STATE_CONNECTED: + if (mServerSession != null) { + mServerSession.close(); + mServerSession = null; + } + try { + closeSocket(false, true); + mConnSocket = null; + } catch (IOException ex) { + Log.e(TAG, "Caught the error: " + ex); + } + setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); + break; + default: + break; + } + } + } + }; +} |