Bluetooth: Introduce BTC Service
Introduce the BTC Service which listen to the Bluetooth
related events and send those events to the connected client.
BT-WLAN coex module would connect to this socket as client and listen
to BT related events
Change-Id: Ic8223d6c761bb9e64ce94b83b2a947c1ad87b1d4
diff --git a/Android.mk b/Android.mk
new file mode 100755
index 0000000..ebe45cd
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BluetoothQcom
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100755
index 0000000..3474e2e
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="qcom.android.bluetooth"
+ android:sharedUserId="android.uid.bluetooth">
+
+ <original-package android:name="qcom.android.bluetooth" />
+
+ <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+ <application>
+ <service
+ android:name = ".btcservice.BTCService">
+ </service>
+ <receiver
+ android:exported="true"
+ android:name=".btcservice.BTCEventProvider">
+ <intent-filter>
+ <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+ <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
+ <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
+ <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
+ <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+ <action android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
+ <action android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
+ <action android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ <action android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
+ <action android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/src/qcom/android/bluetooth/btcservice/BTCEventProvider.java b/src/qcom/android/bluetooth/btcservice/BTCEventProvider.java
new file mode 100755
index 0000000..ded8d3c
--- /dev/null
+++ b/src/qcom/android/bluetooth/btcservice/BTCEventProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package qcom.android.bluetooth.btcservice;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothInputDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import java.lang.Object;
+/**
+ * <p> {@link com.android.bluetooth.btservice.BTCEventProvider} is a BroadcastReceiver
+ * which listens to set of Bluetoth related events which required by BTWlan Coex module
+ * to achieve BT WLAN co-existance
+ * </p>
+ * <p> BTEventProvider is responsible for starting and stopping of
+ * {@link com.android.bluetooth.btservice.BTCService} and
+ * also to send the events via {@link com.android.bluetooth.btservice.BTCService#sendEvent}
+ */
+
+public class BTCEventProvider extends BroadcastReceiver {
+ private static final String TAG = "BTCEventProvider";
+ private static final boolean D = /*Constants.DEBUG*/true;
+ private static final boolean V = true/*Constants.VERBOSE*/;
+ private int state;
+ BTCService.BTCEvent event = BTCService.BTCEvent.BLUETOOTH_NONE;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ state = intent.getIntExtra
+ (BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothAdapter.STATE_ON == state) {
+ if (V) Log.v(TAG, "Received BLUETOOTH_STATE_ON");
+ ComponentName service = context.startService
+ (new Intent(context, BTCService.class));
+ if (service != null) {
+ event = BTCService.BTCEvent.BLUETOOTH_ON;
+ } else {
+ Log.e(TAG, "Could Not Start BTC Service ");
+ return;
+ }
+ } else if (BluetoothAdapter.STATE_OFF == state) {
+ if (V) Log.v(TAG, "Received BLUETOOTH_STATE_OFF");
+ event = BTCService.BTCEvent.BLUETOOTH_OFF;
+ }
+ } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
+ if (V) Log.v(TAG, "Received ACTION_DISCOVERY_STARTED");
+ event = BTCService.BTCEvent.BLUETOOTH_DISCOVERY_STARTED;
+ } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
+ if (V) Log.v(TAG, "Received ACTION_DISCOVERY_STOPPED");
+ event = BTCService.BTCEvent.BLUETOOTH_DISCOVERY_FINISHED;
+ } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
+ if (V) Log.v(TAG, "Received ACTION_ACL_CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_DEVICE_CONNECTED;
+ } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
+ if (V) Log.v(TAG, "Received ACTION_ACL_DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_DEVICE_DISCONNECTED;
+ } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+ if (V) Log.v(TAG, "ACTION_CONNECTION_STATE_CHANGED");
+ state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothProfile.STATE_CONNECTED == state) {
+ if (V) Log.v(TAG, "BluetoothHeadset.STATE_CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_HEADSET_CONNECTED;
+ } else if (BluetoothProfile.STATE_DISCONNECTED == state) {
+ if (V) Log.v(TAG, "BluetoothHeadset.STATE_DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_HEADSET_DISCONNECTED;
+ }
+ } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+ if (V) Log.v(TAG, "BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED");
+ state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothProfile.STATE_CONNECTED == state) {
+ if (V) Log.v(TAG, "ACTION_AUDIO_STATE_CHANGED:STATE_CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_HEADSET_AUDIO_STREAM_STARTED;
+ } else if (BluetoothProfile.STATE_DISCONNECTED == state) {
+ if (V) Log.v(TAG, "ACTION_AUDIO_STATE_CHANGED:STATE_DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_HEADSET_AUDIO_STREAM_STOPPED;
+ }
+ } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+ if (V) Log.v(TAG, "BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED");
+ state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothProfile.STATE_CONNECTED == state) {
+ if (V) Log.v(TAG, "A2DP: STATE_CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_AUDIO_SINK_CONNECTED;
+ } else if (BluetoothProfile.STATE_DISCONNECTED == state) {
+ if (V) Log.v(TAG, "A2DP: STATE_DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_AUDIO_SINK_DISCONNECTED;
+ }
+ } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
+ if (V) Log.v(TAG, "BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED");
+ state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothA2dp.STATE_PLAYING == state) {
+ if (V) Log.v(TAG, "A2DP. PLAYING_STATE_CHANGED: CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_SINK_STREAM_STARTED;
+ } else if (BluetoothA2dp.STATE_NOT_PLAYING == state) {
+ if (V) Log.v(TAG, "A2DP.PLAYING_STATE_CHANGED: DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_SINK_STREAM_STOPPED;
+ }
+ } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) {
+ if (V) Log.v(TAG, "HID.ACTION_CONNECTION_STATE_CHANGED");
+ state = intent.getIntExtra
+ (BluetoothProfile.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (BluetoothProfile.STATE_CONNECTED == state) {
+ if (V) Log.v(TAG, "HID.CONNECTION_STATE_CHANGED: CONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_INPUT_DEVICE_CONNECTED;
+ } else if (BluetoothProfile.STATE_DISCONNECTED == state) {
+ if (V) Log.v(TAG, "HID.CONNECTION_STATE_CHANGED:DISCONNECTED");
+ event = BTCService.BTCEvent.BLUETOOTH_INPUT_DEVICE_DISCONNECTED;
+ }
+ }
+ //Don;t send if it is not relavant event
+ if (event != BTCService.BTCEvent.BLUETOOTH_NONE) {
+ int status = BTCService.sendEvent(event);
+ if (status == 0) {
+ if (V) Log.v(TAG, "Event:" + event.getValue() + "Written Succesfully");
+ }
+ else {
+ Log.e(TAG, "Error while sending Event:" + event.getValue());
+ }
+ if (event == BTCService.BTCEvent.BLUETOOTH_OFF) {
+ Log.v(TAG, "Stop the BTC Service");
+ context.stopService(new Intent(context, BTCService.class));
+ }
+ }
+ }
+}
diff --git a/src/qcom/android/bluetooth/btcservice/BTCService.java b/src/qcom/android/bluetooth/btcservice/BTCService.java
new file mode 100755
index 0000000..3b453ad
--- /dev/null
+++ b/src/qcom/android/bluetooth/btcservice/BTCService.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of The Linux Foundation nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package qcom.android.bluetooth.btcservice;
+
+import android.app.Service;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.Credentials;
+import java.io.OutputStream;
+import android.util.Log;
+import android.os.IBinder;
+import android.content.Intent;
+import android.os.Process;
+import java.nio.ByteBuffer;
+
+/**
+ * <p> {@link com.android.bluetooth.btservice.BTCService} is a background service which starts
+ * and stops on BLUETOOTH_ON and BLUETOOTH_OFF intents
+ * Ref: {@see android.bluetooth.BluetoothAdapter#ACTION_STATE_CHANGED}
+ * </p>
+ * <p> {@link com.android.bluetooth.btservice.BTCEventProvider} is the BroadcaseReciever which
+ * listen to set of bluetooth related intents and responsible for starting and stopping of
+ * BTCService
+ * </p>
+ * <p> {@link com.android.bluetooth.btservice.BTCService} creates the Local Socket server named as
+ * {@link com.android.bluetooth.btservice.BTCService.BTC_SERVER} and listen for the
+ * incoming connections up on start
+ *
+ * <p> {@link com.android.bluetooth.btservice.BTCService} can accepts the connection from clients
+ * whose uid is either {@link android.os.Process.BLUETOOTH_UID} or ROOT(with UID 0) and discards
+ * all other connections and hence provides the events only to the authenticates processes
+ * </p>
+ *
+ * Once the successful connection is established BTCService pushes the events listed in {@link BTCEvent}
+ * over the socket, Client has to read these data and interpret based on the values {@link BTCEvent}
+ * </p>
+ *
+ * <p> As LocalSocket implementation doesn't properly propagate the closing of socket to
+ * to the native layets properly, closing the socket at Java layer doesn't invalidate the
+ * socket at the native layers
+ * *** So, It is client's responsibility to close their socket
+ * end just after recieving the {@link BTCEvent.BLUETOOTH_OFF} event
+ * </p>
+ *
+ */
+public class BTCService extends Service
+{
+ public final static String BTC_SERVER = "qcom.btc.server";
+ private static LocalServerSocket mSocket =null;
+ private static LocalSocket mRemoteSocket = null;
+ private static final String LOGTAG = "BTCService";
+ private static OutputStream mOutputStream = null;
+ private static Thread mSocketAcceptThread;
+ private static boolean mLocalConnectInitiated = false;
+ private static final Object mLock = new Object();
+
+ public enum BTCEvent {
+ BLUETOOTH_NONE(0x00),
+ BLUETOOTH_ON(0x20),
+ BLUETOOTH_OFF(0x21),
+ BLUETOOTH_DISCOVERY_STARTED(0x22),
+ BLUETOOTH_DISCOVERY_FINISHED(0x23),
+ BLUETOOTH_DEVICE_CONNECTED(0x24),
+ BLUETOOTH_DEVICE_DISCONNECTED(0x25),
+ BLUETOOTH_HEADSET_CONNECTED(0x40),
+ BLUETOOTH_HEADSET_DISCONNECTED(0x41),
+ BLUETOOTH_HEADSET_AUDIO_STREAM_STARTED(0x42),
+ BLUETOOTH_HEADSET_AUDIO_STREAM_STOPPED(0x43),
+ BLUETOOTH_AUDIO_SINK_CONNECTED(0x60),
+ BLUETOOTH_AUDIO_SINK_DISCONNECTED(0x61),
+ BLUETOOTH_SINK_STREAM_STARTED(0x62),
+ BLUETOOTH_SINK_STREAM_STOPPED(0x63),
+ BLUETOOTH_INPUT_DEVICE_CONNECTED(0x80),
+ BLUETOOTH_INPUT_DEVICE_DISCONNECTED(0x81);
+
+ private int mValue;
+ private BTCEvent(int value) {
+ mValue = value;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+ }
+
+ public BTCService() {
+ Log.v(LOGTAG, "BTCService");
+ }
+
+ static private void cleanupService() throws java.io.IOException {
+ synchronized (mLock) {
+ if (mSocket != null) {
+ mSocket.close();
+ mSocket = null;
+ Log.v(LOGTAG, "Server Socket closed");
+ }
+ if (mRemoteSocket != null) {
+ mRemoteSocket.close();
+ mRemoteSocket = null;
+ Log.v(LOGTAG, "Client Socket closed");
+ }
+ if( mSocketAcceptThread != null ) {
+ mSocketAcceptThread.interrupt();
+ Log.v(LOGTAG, "Acceptor thread stopped");
+ }
+ }
+ }
+
+ /**
+ * <p> Sends the BTCEvent over the valid socket connection
+ * </p>
+ * @param event {@link BTCEvent} object
+ * @return 0 on success,
+ * -1 when there is no client connection available,
+ * -2 when there is issue while writing on client socket.
+ */
+ static public int sendEvent(BTCEvent event) {
+ Log.v(LOGTAG, "sendEvent: " + event.getValue());
+ if (mRemoteSocket == null) {
+ Log.v(LOGTAG, "No Valid Connection" );
+ return -1;
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(4);
+ buffer.putInt(event.getValue());
+
+ try {
+ mOutputStream.write(buffer.array());
+ }
+ catch (java.io.IOException e) {
+ Log.e(LOGTAG, "Error while posting the event: " + event + "Error:" + e);
+ Log.e(LOGTAG, "connectoin is closed for Unknown reason, restart the acceptor thread ");
+ startListener();
+ return -2;
+ }
+ return 0;
+ }
+
+ static private void startListener() {
+ try {
+ cleanupService();
+ mSocket = new LocalServerSocket(BTC_SERVER);
+ } catch (java.io.IOException e) {
+ Log.e(LOGTAG, "Error While cleanupService:"+ e);
+ return;
+ }
+ mSocketAcceptThread = new Thread() {
+ @Override
+ public void run() {
+ do {
+ try {
+ Log.v(LOGTAG, "Waiting for connection...");
+ try {
+ mRemoteSocket = mSocket.accept();
+ } catch (java.io.IOException e) {
+ Log.e(LOGTAG, "Error While accepting the connection: " + e);
+ //Keep back in accpetor loop
+ mRemoteSocket = null;
+ }
+ if (mLocalConnectInitiated) {
+ //This is just a local connection
+ //to unblock the accept
+ Log.e(LOGTAG, "Terminator in action: ");
+ cleanupService();
+ mLocalConnectInitiated = false;
+ return;
+ }
+ if (mRemoteSocket != null) {
+ Log.v(LOGTAG, "Got socket: " + mRemoteSocket);
+ Credentials cred = null;
+ try {
+ cred = mRemoteSocket.getPeerCredentials();
+ } catch (java.io.IOException e) {
+ Log.e(LOGTAG, "Getting peer credential" + "Error:" + e);
+ cred = null;//Pas through, so that it fails and back in loop
+ }
+ if (cred != null) {
+ if (cred.getUid() != 0 && cred.getUid() != Process.BLUETOOTH_UID) {
+ Log.e(LOGTAG, "Peer with UID: " + cred.getUid() +" is not authenticated");
+ Log.e(LOGTAG, "Close the connection and back in acceptor loop");
+ mRemoteSocket.close();
+ mRemoteSocket = null;
+ } else {
+ //Only ROOT and BLUETOOTH connections are
+ //are accepted
+ Log.v(LOGTAG, "Authenticated socket: " + mRemoteSocket);
+ mOutputStream = mRemoteSocket.getOutputStream();
+ //Close the Server Socket
+ mSocket.close();
+ mSocket = null;
+ return;
+ }
+ } else {
+ Log.e(LOGTAG, "Not able to get the credentais, discard connection");
+ mRemoteSocket.close();
+ mRemoteSocket = null;
+ }
+
+ } else {
+ Log.e(LOGTAG, "Remote socket unavaialble");
+ //Keep back in accpetor loop
+ mRemoteSocket = null;
+ }
+ } catch (Exception e) {
+ Log.d(LOGTAG, "RunningThread InterruptedException");
+ e.printStackTrace();
+ Thread.currentThread().interrupt();
+ }
+ } while ((!Thread.currentThread().isInterrupted()));
+ }
+ };
+ mSocketAcceptThread.start();
+ }
+
+ private void closeServerSocket() {
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ LocalSocketAddress locSockAddr = new LocalSocketAddress(BTC_SERVER);
+ LocalSocket terminator = new LocalSocket();
+ Log.e(LOGTAG, "Trying to unblock by connecting");
+ mLocalConnectInitiated = true;
+ terminator.connect(locSockAddr);
+ terminator.close();
+ Log.e(LOGTAG, "Terminator closed");
+ } catch (java.io.IOException e) {
+ Log.e(LOGTAG, "cant connect: " + e);
+ }
+ }
+ };
+ t.start();
+ }
+
+ @Override
+ public void onCreate() {
+ Log.v(LOGTAG, "onCreate");
+ super.onCreate();
+
+ Log.v(LOGTAG, "calling startListener");
+ startListener();
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.v(LOGTAG, "onDestroy");
+ try {
+
+ if (mSocket != null) {
+ //This is the case where
+ //server socket is still stuck in accept()
+ closeServerSocket();
+ }
+ cleanupService();
+ }
+ catch (java.io.IOException e) {
+ Log.e(LOGTAG, "in onDestroy: " + e);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent in) {
+ Log.v(LOGTAG, "onBind");
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(LOGTAG, "onStart Command called!!");
+ //Make this restarable service by
+ //Android app manager
+ return START_STICKY;
+ }
+}