summaryrefslogtreecommitdiffstats
path: root/src/com/android/bluetooth/map/BluetoothMapService.java
diff options
context:
space:
mode:
authorMatthew Xie <mattx@google.com>2013-07-18 18:18:36 -0700
committerZhihai Xu <zhihaixu@google.com>2013-08-09 18:44:53 -0700
commitfd6603b8bf9ed72dcc8bd59aaef3209251b6e17c (patch)
treefecaf3c95adce97dc5176cc341d903fe488d5edf /src/com/android/bluetooth/map/BluetoothMapService.java
parentbb1ac417208c8e283f9b5b49f4413856500ed0f9 (diff)
downloadandroid_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.java778
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;
+ }
+ }
+ }
+ };
+}