diff options
author | fredc <fredc@broadcom.com> | 2012-05-07 00:10:27 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-07-16 22:05:28 -0700 |
commit | 74ae04c73312403e89db0f8e9bd9601d403b4783 (patch) | |
tree | 60bc40da3e469998bded767fa88e86bb4574cbfc /src/com | |
parent | ce4edb8ed132976651b32b337f3d90e1a170afa9 (diff) | |
download | android_packages_apps_Bluetooth-74ae04c73312403e89db0f8e9bd9601d403b4783.tar.gz android_packages_apps_Bluetooth-74ae04c73312403e89db0f8e9bd9601d403b4783.tar.bz2 android_packages_apps_Bluetooth-74ae04c73312403e89db0f8e9bd9601d403b4783.zip |
Fixed memory leak in Binder objects from repeatedly turning on/off Bluetooth
Change-Id: Ibee5382816d47b1e9b846461942a9daccc4210a7
Diffstat (limited to 'src/com')
22 files changed, 1702 insertions, 994 deletions
diff --git a/src/com/android/bluetooth/Utils.java b/src/com/android/bluetooth/Utils.java index 477f8c5e4..7ace4cd3c 100644 --- a/src/com/android/bluetooth/Utils.java +++ b/src/com/android/bluetooth/Utils.java @@ -5,6 +5,7 @@ package com.android.bluetooth; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.os.ParcelUuid; import java.nio.ByteBuffer; @@ -29,6 +30,10 @@ final public class Utils { address[5]); } + public static byte[] getByteAddress(BluetoothDevice device) { + return getBytesFromAddress(device.getAddress()); + } + public static byte[] getBytesFromAddress(String address) { int i, j = 0; byte[] output = new byte[BD_ADDR_LEN]; diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java index bd0d0bb98..bce55f72d 100755 --- a/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/src/com/android/bluetooth/a2dp/A2dpService.java @@ -3,24 +3,17 @@ */ package com.android.bluetooth.a2dp; -import android.app.Service; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothA2dp; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; import android.provider.Settings; import android.util.Log; +import java.util.ArrayList; import java.util.List; import java.util.Iterator; import java.util.Map; -import android.content.pm.PackageManager; -import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; /** @@ -33,110 +26,173 @@ public class A2dpService extends ProfileService { private A2dpStateMachine mStateMachine; + protected String getName() { + return TAG; + } + + protected IProfileServiceBinder initBinder() { + return new BluetoothA2dpBinder(this); + } protected boolean start() { - if (DBG) log("startService()"); - mStateMachine = new A2dpStateMachine(this); + mStateMachine = new A2dpStateMachine(this,this); mStateMachine.start(); return true; } protected boolean stop() { - if (DBG) log("stopService()"); + mStateMachine.quit(); + return true; + } + + protected boolean cleanup() { if (mStateMachine!= null) { - mStateMachine.quit(); mStateMachine.cleanup(); mStateMachine=null; } return true; } - protected String getName() { - return TAG; + //API Methods + boolean connect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { + return false; + } + + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState == BluetoothProfile.STATE_CONNECTED || + connectionState == BluetoothProfile.STATE_CONNECTING) { + return false; + } + + mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); + return true; } - @Override - public IBinder onBind(Intent intent) { - log("onBind"); - return mBinder; + boolean disconnect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState != BluetoothProfile.STATE_CONNECTED && + connectionState != BluetoothProfile.STATE_CONNECTING) { + return false; + } + + mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device); + return true; } - /** - * Handlers for incoming service calls - */ - private final IBluetoothA2dp.Stub mBinder = new IBluetoothA2dp.Stub() { + List<BluetoothDevice> getConnectedDevices() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getConnectedDevices(); + } - public boolean connect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); + List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getDevicesMatchingConnectionStates(states); + } - if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { - return false; - } + int getConnectionState(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getConnectionState(device); + } + + boolean setPriority(BluetoothDevice device, int priority) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), + priority); + if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority); + return true; + } - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState == BluetoothProfile.STATE_CONNECTED || - connectionState == BluetoothProfile.STATE_CONNECTING) { - return false; + int getPriority(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int priority = Settings.Secure.getInt(getContentResolver(), + Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), + BluetoothProfile.PRIORITY_UNDEFINED); + return priority; + } + + synchronized boolean isA2dpPlaying(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + if (DBG) Log.d(TAG, "isA2dpPlaying(" + device + ")"); + return mStateMachine.isPlaying(device); + } + + //Binder object: Must be static class or memory leak may occur + private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub + implements IProfileServiceBinder { + private A2dpService mService; + + private A2dpService getService() { + if (mService != null && mService.isAvailable()) { + return mService; } + return null; + } - mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); + BluetoothA2dpBinder(A2dpService svc) { + mService = svc; + } + + public boolean cleanup() { + mService = null; return true; } - public boolean disconnect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState != BluetoothProfile.STATE_CONNECTED && - connectionState != BluetoothProfile.STATE_CONNECTING) { - return false; - } + public boolean connect(BluetoothDevice device) { + A2dpService service = getService(); + if (service == null) return false; + return service.connect(device); + } - mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device); - return true; + public boolean disconnect(BluetoothDevice device) { + A2dpService service = getService(); + if (service == null) return false; + return service.disconnect(device); } public List<BluetoothDevice> getConnectedDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getConnectedDevices(); + A2dpService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getConnectedDevices(); } public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getDevicesMatchingConnectionStates(states); + A2dpService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getDevicesMatchingConnectionStates(states); } public int getConnectionState(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getConnectionState(device); + A2dpService service = getService(); + if (service == null) return BluetoothProfile.STATE_DISCONNECTED; + return service.getConnectionState(device); } public boolean setPriority(BluetoothDevice device, int priority) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - Settings.Secure.putInt(getContentResolver(), - Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), - priority); - if (DBG) log("Saved priority " + device + " = " + priority); - return true; + A2dpService service = getService(); + if (service == null) return false; + return service.setPriority(device, priority); } public int getPriority(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int priority = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), - BluetoothProfile.PRIORITY_UNDEFINED); - return priority; + A2dpService service = getService(); + if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; + return service.getPriority(device); } - public synchronized boolean isA2dpPlaying(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); - if (DBG) log("isA2dpPlaying(" + device + ")"); - return mStateMachine.isPlaying(device); + public boolean isA2dpPlaying(BluetoothDevice device) { + A2dpService service = getService(); + if (service == null) return false; + return service.isA2dpPlaying(device); } }; - } diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java index c7debf407..59df75787 100755 --- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java @@ -52,6 +52,7 @@ final class A2dpStateMachine extends StateMachine { private Pending mPending; private Connected mConnected; + private A2dpService mService; private Context mContext; private BluetoothAdapter mAdapter; private static final ParcelUuid[] A2DP_UUIDS = { @@ -89,8 +90,9 @@ final class A2dpStateMachine extends StateMachine { classInitNative(); } - A2dpStateMachine(Context context) { + A2dpStateMachine(A2dpService svc, Context context) { super(TAG); + mService = svc; mContext = context; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -109,7 +111,9 @@ final class A2dpStateMachine extends StateMachine { public void cleanup() { cleanupNative(); - if(mContext != null) + if(mService != null) + mService = null; + if (mContext != null) mContext = null; if(mAdapter != null) mAdapter = null; @@ -600,10 +604,7 @@ final class A2dpStateMachine extends StateMachine { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); - AdapterService svc = AdapterService.getAdapterService(); - if (svc != null) { - svc.onProfileConnectionStateChanged(device, BluetoothProfile.A2DP, newState, prevState); - } + mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP, newState, prevState); } private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { @@ -659,7 +660,7 @@ final class A2dpStateMachine extends StateMachine { final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; - // Do not modify without upating the HAL bt_av.h files. + // Do not modify without updating the HAL bt_av.h files. // match up with btav_connection_state_t enum of bt_av.h final static int CONNECTION_STATE_DISCONNECTED = 0; diff --git a/src/com/android/bluetooth/btservice/AdapterApp.java b/src/com/android/bluetooth/btservice/AdapterApp.java index b419fcd30..be5d76998 100644 --- a/src/com/android/bluetooth/btservice/AdapterApp.java +++ b/src/com/android/bluetooth/btservice/AdapterApp.java @@ -14,12 +14,24 @@ import android.util.Log; public class AdapterApp extends Application { private static final String TAG = "BluetoothAdapterApp"; private static final boolean DBG = true; + //For Debugging only + private static int sRefCount=0; static { if (DBG) Log.d(TAG,"Loading JNI Library"); System.loadLibrary("bluetooth_jni"); } + public AdapterApp() { + super(); + if (DBG) { + synchronized (AdapterApp.class) { + sRefCount++; + Log.d(TAG, "REFCOUNT: Constructed "+ this + " Instance Count = " + sRefCount); + } + } + } + @Override public void onCreate() { super.onCreate(); @@ -27,8 +39,12 @@ public class AdapterApp extends Application { } @Override - protected void finalize() throws Throwable { - super.finalize(); - if (DBG) Log.d(TAG, "finalize"); + protected void finalize() { + if (DBG) { + synchronized (AdapterApp.class) { + sRefCount--; + Log.d(TAG, "REFCOUNT: Finalized: " + this +", Instance Count = " + sRefCount); + } + } } } diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java index aea4e5bfc..d061138c1 100755 --- a/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -38,9 +38,7 @@ class AdapterProperties { private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; private int mState = BluetoothAdapter.STATE_OFF; - //private static AdapterProperties sInstance; private AdapterService mService; - private Context mContext; private boolean mDiscovering; private RemoteDevices mRemoteDevices; @@ -49,11 +47,9 @@ class AdapterProperties { // can be added here. private Object mObject = new Object(); - AdapterProperties(AdapterService service, Context context) { + public AdapterProperties(AdapterService service) { mService = service; - mContext = context; } - public void init(RemoteDevices remoteDevices) { if (mProfileConnectionState ==null) { mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>(); @@ -64,13 +60,12 @@ class AdapterProperties { } public void cleanup() { + mRemoteDevices = null; if (mProfileConnectionState != null) { mProfileConnectionState.clear(); mProfileConnectionState = null; } - mRemoteDevices = null; mService = null; - mContext = null; } public Object Clone() throws CloneNotSupportedException { @@ -257,7 +252,7 @@ class AdapterProperties { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, convertToAdapterState(prevState)); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); debugLog("CONNECTION_STATE_CHANGE: " + device + ": " + prevState + " -> " + state); } @@ -389,7 +384,7 @@ class AdapterProperties { intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); debugLog("Name is: " + mName); break; case AbstractionLayer.BT_PROPERTY_BDADDR: @@ -406,7 +401,7 @@ class AdapterProperties { intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); debugLog("Scan Mode:" + mScanMode); if (mBluetoothDisabling) { mBluetoothDisabling=false; @@ -488,11 +483,11 @@ class AdapterProperties { if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) { mDiscovering = false; intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); - mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) { mDiscovering = true; intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED); - mContext.sendBroadcast(intent, mService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, mService.BLUETOOTH_PERM); } } } diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java index b78da08d7..7ddf5f96b 100755 --- a/src/com/android/bluetooth/btservice/AdapterService.java +++ b/src/com/android/bluetooth/btservice/AdapterService.java @@ -52,6 +52,9 @@ import android.os.ServiceManager; public class AdapterService extends Service { private static final String TAG = "BluetoothAdapterService"; private static final boolean DBG = true; + private static final boolean TRACE_REF = true; + //For Debugging only + private static int sRefCount=0; /** * List of profile services to support.Comment out to disable a profile @@ -81,25 +84,61 @@ public class AdapterService extends Service { } private static AdapterService sAdapterService; - public static AdapterService getAdapterService(){ - return sAdapterService; + public static synchronized AdapterService getAdapterService(){ + if (sAdapterService != null && !sAdapterService.mCleaningUp) { + if (DBG) Log.d(TAG, "getAdapterService(): returning " + sAdapterService); + return sAdapterService; + } + if (DBG) { + if (sAdapterService == null) { + Log.d(TAG, "getAdapterService(): service not available"); + } else if (sAdapterService.mCleaningUp) { + Log.d(TAG,"getAdapterService(): service is cleaning up"); + } + } + return null; } - private IBluetoothManager mBluetoothManager; - private IBluetooth mBluetoothService; - private AdapterProperties mAdapterProperties; - private int mAdapterState; - private Context mContext; + private static synchronized void setAdapterService(AdapterService instance) { + if (instance != null && !instance.mCleaningUp) { + if (DBG) Log.d(TAG, "setAdapterService(): set to: " + sAdapterService); + sAdapterService = instance; + } else { + if (DBG) { + if (sAdapterService == null) { + Log.d(TAG, "setAdapterService(): service not available"); + } else if (sAdapterService.mCleaningUp) { + Log.d(TAG,"setAdapterService(): service is cleaning up"); + } + } + } + } + + private static synchronized void clearAdapterService() { + sAdapterService = null; + } + private AdapterProperties mAdapterProperties; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; private JniCallbacks mJniCallbacks; private RemoteDevices mRemoteDevices; private boolean mProfilesStarted; private boolean mNativeAvailable; + private boolean mCleaningUp; private HashMap<String,Integer> mProfileServicesState = new HashMap<String,Integer>(); private int mCurrentRequestId; + public AdapterService() { + super(); + if (TRACE_REF) { + synchronized (AdapterService.class) { + sRefCount++; + Log.d(TAG, "REFCOUNT: CREATED. INSTANCE_COUNT" + sRefCount); + } + } + } + public void onProfileConnectionStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) { Message m = mHandler.obtainMessage(MESSAGE_PROFILE_CONNECTION_STATE_CHANGED); m.obj = device; @@ -112,10 +151,10 @@ public class AdapterService extends Service { } private void processProfileStateChanged(BluetoothDevice device, int profileId, int newState, int prevState) { - if (mBluetoothService != null) { + IBluetooth.Stub binder = mBinder; + if (binder != null) { try { - mBluetoothService.sendConnectionStateChange(device, profileId, newState, - prevState); + binder.sendConnectionStateChange(device, profileId, newState,prevState); } catch (RemoteException re) { Log.e(TAG, "",re); } @@ -167,7 +206,9 @@ public class AdapterService extends Service { } } if (DBG) Log.d(TAG, "All profile services stopped..."); - onProfilesStopped(); + //Send message to state machine + mProfilesStarted=false; + mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STOPPED)); } else if (isTurningOn) { //Process start pending //Check if all services are started if so, update state @@ -183,7 +224,9 @@ public class AdapterService extends Service { } } if (DBG) Log.d(TAG, "All profile services started."); - onProfilesStarted(); + mProfilesStarted=true; + //Send message to state machine + mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED)); } } @@ -191,11 +234,10 @@ public class AdapterService extends Service { public void onCreate() { super.onCreate(); if (DBG) debugLog("onCreate"); - mContext = this; - sAdapterService = this; - mAdapterProperties = new AdapterProperties(this, mContext); - mAdapterStateMachine = new AdapterState(this, mContext, mAdapterProperties); - mJniCallbacks = JniCallbacks.getInstance(null, mAdapterProperties,mAdapterStateMachine,null); + mBinder = new AdapterServiceBinder(this); + mAdapterProperties = new AdapterProperties(this); + mAdapterStateMachine = new AdapterState(this, mAdapterProperties); + mJniCallbacks = new JniCallbacks(mAdapterStateMachine, mAdapterProperties); initNative(); mNativeAvailable=true; mAdapterStateMachine.start(); @@ -203,6 +245,7 @@ public class AdapterService extends Service { //Load the name and address getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR); getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME); + } @Override @@ -222,8 +265,11 @@ public class AdapterService extends Service { } public int onStartCommand(Intent intent ,int flags, int startId) { - mCurrentRequestId = startId; + if (mCleaningUp) { + Log.e(TAG,"*************Received new request while service is cleaning up****************************"); + } + if (DBG) debugLog("onStartCommand: flags = " + flags + ", startId = " + startId); if (checkCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM)!=PackageManager.PERMISSION_GRANTED) { Log.e(TAG, "Permission denied!"); @@ -275,89 +321,40 @@ public class AdapterService extends Service { for (int i=0; i < SUPPORTED_PROFILE_SERVICES.length;i++) { mProfileServicesState.put(SUPPORTED_PROFILE_SERVICES[i].getName(),BluetoothAdapter.STATE_OFF); } - - mRemoteDevices = new RemoteDevices(this, mContext); - mRemoteDevices.init(); + mRemoteDevices = new RemoteDevices(this); + mBondStateMachine = new BondStateMachine(this, mAdapterProperties, mRemoteDevices); mAdapterProperties.init(mRemoteDevices); - mBondStateMachine = new BondStateMachine(this, mContext, mAdapterProperties, mRemoteDevices); - mJniCallbacks.init(mRemoteDevices, mAdapterProperties,mAdapterStateMachine,mBondStateMachine); - - //Init BluetoothManager - if (DBG) {debugLog("processStart(): Initializing Bluetooth Manager");} - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - mBluetoothManager = IBluetoothManager.Stub.asInterface(b); - if (mBluetoothManager != null) { - try { - Log.d(TAG, "FRED: Registering manager callback " + mManagerCallback); - mBluetoothService = mBluetoothManager.registerAdapter(mManagerCallback); - } catch (RemoteException re) { - Log.e(TAG, "",re); - } - } - } + mJniCallbacks.init(mBondStateMachine,mRemoteDevices); //Start Bond State Machine if (DBG) {debugLog("processStart(): Starting Bond State Machine");} mBondStateMachine.start(); + //FIXME: Set static instance here??? + setAdapterService(this); + //Start profile services - if (SUPPORTED_PROFILE_SERVICES.length==0 || mProfilesStarted) { - //Skip starting profiles and go to next step - if (DBG) {debugLog("processStart(): Profile Services started");} - Message m = mAdapterStateMachine.obtainMessage(AdapterState.STARTED); - mAdapterStateMachine.sendMessage(m); - } else { + if (!mProfilesStarted && SUPPORTED_PROFILE_SERVICES.length >0) { //Startup all profile services setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_ON); + }else { + if (DBG) {debugLog("processStart(): Profile Services alreay started");} + mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.STARTED)); } } void startBluetoothDisable() { - Log.d(TAG,"startBluetoothDisable()"); - Message m = mAdapterStateMachine.obtainMessage(AdapterState.BEGIN_DISABLE); - mAdapterStateMachine.sendMessage(m); - } - - void onProfilesStarted(){ - Log.d(TAG,"onProfilesStarted()"); - mProfilesStarted=true; - Message m = mAdapterStateMachine.obtainMessage(AdapterState.STARTED); - mAdapterStateMachine.sendMessage(m); - } - - void onProfilesStopped() { - Log.d(TAG,"onProfilesStopped()"); - mProfilesStarted=false; - //Message m = mAdapterStateMachine.obtainMessage(AdapterState.DISABLE); - Message m = mAdapterStateMachine.obtainMessage(AdapterState.STOPPED); - mAdapterStateMachine.sendMessage(m); + mAdapterStateMachine.sendMessage(mAdapterStateMachine.obtainMessage(AdapterState.BEGIN_DISABLE)); } - boolean stopProfileServices() { - if (SUPPORTED_PROFILE_SERVICES.length==0 || !mProfilesStarted) { - if (DBG) {debugLog("processDisable(): No profiles services to stop or already stopped.");} + if (mProfilesStarted && SUPPORTED_PROFILE_SERVICES.length>0) { + setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_OFF); + return true; + } else { + if (DBG) {debugLog("stopProfileServices(): No profiles services to stop or already stopped.");} return false; } - setProfileServiceState(SUPPORTED_PROFILE_SERVICES,BluetoothAdapter.STATE_OFF); - return true; - } - - void processStopped() { - Log.d(TAG, "processStopped()"); - - if (mBluetoothManager != null) { - try { - Log.d(TAG,"FRED: Unregistering manager callback " + mManagerCallback); - mBluetoothManager.unregisterAdapter(mManagerCallback); - } catch (RemoteException re) { - Log.e(TAG, "",re); - } - } - mBondStateMachine.quit(); - mBondStateMachine.cleanup(); - mBondStateMachine = null; } void startShutdown(int requestId) { @@ -366,7 +363,6 @@ public class AdapterService extends Service { Log.w(TAG, "Ignoring shutdown request. Invalid requestId"); return; } - Message m = mHandler.obtainMessage(MESSAGE_SHUTDOWN); synchronized(mHandler) { mHandler.sendMessageDelayed(m, SHUTDOWN_TIMEOUT); @@ -376,50 +372,61 @@ public class AdapterService extends Service { void cleanup () { if (DBG)debugLog("cleanup()"); - - if (mNativeAvailable) { - Log.d(TAG, "Cleaning up adapter native...."); - cleanupNative(); - Log.d(TAG, "Done cleaning up adapter native...."); - mNativeAvailable=false; + if (mCleaningUp) { + Log.w(TAG,"*************service already starting to cleanup... Ignoring cleanup request........."); + return; } + mCleaningUp = true; + if (mAdapterStateMachine != null) { mAdapterStateMachine.quit(); mAdapterStateMachine.cleanup(); mAdapterStateMachine = null; } + if (mBondStateMachine != null) { + mBondStateMachine.quit(); + mBondStateMachine.cleanup(); + mBondStateMachine = null; + } + if (mRemoteDevices != null) { mRemoteDevices.cleanup(); mRemoteDevices = null; } + if (mNativeAvailable) { + Log.d(TAG, "Cleaning up adapter native...."); + cleanupNative(); + Log.d(TAG, "Done cleaning up adapter native...."); + mNativeAvailable=false; + } + if (mAdapterProperties != null) { mAdapterProperties.cleanup(); mAdapterProperties = null; } - mProfileServicesState.clear(); - if (DBG)debugLog("cleanup() done"); - } + if (mJniCallbacks != null) { + mJniCallbacks.cleanup(); + mJniCallbacks = null; + } - final private IBluetoothManagerCallback mManagerCallback = - new IBluetoothManagerCallback.Stub() { - public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) Log.d(TAG, "onBluetoothServiceUp"); - synchronized (mManagerCallback) { - mBluetoothService = bluetoothService; - } - } + if (mProfileServicesState != null) { + mProfileServicesState.clear(); + mProfileServicesState= null; + } - public void onBluetoothServiceDown() { - if (DBG) Log.d(TAG, "onBluetoothServiceDown " + this); - synchronized (mManagerCallback) { - mBluetoothService = null; - } - } - }; + //FIXME: Set static instance here??? + clearAdapterService(); + + if (mBinder != null) { + mBinder.cleanup(); + mBinder = null; + } + if (DBG)debugLog("cleanup() done"); + } private static final int MESSAGE_PROFILE_SERVICE_STATE_CHANGED =1; private static final int MESSAGE_PROFILE_CONNECTION_STATE_CHANGED=20; @@ -487,285 +494,525 @@ public class AdapterService extends Service { } } + private boolean isAvailable() { + return !mCleaningUp; + } + /** * Handlers for incoming service calls */ - private final IBluetooth.Stub mBinder = new IBluetooth.Stub() { + private AdapterServiceBinder mBinder; + + /** + * The Binder implementation must be declared to be a static class, with + * the AdapterService instance passed in the constructor. Furthermore, + * when the AdapterService shuts down, the reference to the AdapterService + * must be explicitly removed. + * + * Otherwise, a memory leak can occur from repeated starting/stopping the + * service...Please refer to android.os.Binder for further details on + * why an inner instance class should be avoided. + * + */ + private static class AdapterServiceBinder extends IBluetooth.Stub { + private AdapterService mService; + + public AdapterServiceBinder(AdapterService svc) { + mService = svc; + } + public boolean cleanup() { + mService = null; + return true; + } + + public AdapterService getService() { + if (mService != null && mService.isAvailable()) { + return mService; + } + return null; + } public boolean isEnabled() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getState() == BluetoothAdapter.STATE_ON; + AdapterService service = getService(); + if (service == null) return false; + return service.isEnabled(); } public int getState() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - debugLog("getState(): mAdapterProperties: " + mAdapterProperties); - return mAdapterProperties.getState(); + AdapterService service = getService(); + if (service == null) return BluetoothAdapter.STATE_OFF; + return service.getState(); } public boolean enable() { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - if (DBG) debugLog("enable() called..."); - Message m = - mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON); - m.arg1 = 1; //persist state - mAdapterStateMachine.sendMessage(m); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.enable(); } public boolean disable(boolean persist) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - if (DBG) debugLog("disable() called..."); - int val = (persist ? 1 : 0); - Message m = - mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF); - m.arg1 = val; - mAdapterStateMachine.sendMessage(m); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.disable(persist); } public String getAddress() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - String addrString = null; - byte[] address = mAdapterProperties.getAddress(); - return Utils.getAddressStringFromByte(address); + AdapterService service = getService(); + if (service == null) return null; + return service.getAddress(); } public ParcelUuid[] getUuids() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getUuids(); + AdapterService service = getService(); + if (service == null) return new ParcelUuid[0]; + return service.getUuids(); } public String getName() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); - try { - return mAdapterProperties.getName(); - } catch (Throwable t) { - Log.d(TAG, "Unexpected exception while calling getName()",t); - } - return null; + AdapterService service = getService(); + if (service == null) return null; + return service.getName(); } public boolean setName(String name) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - return mAdapterProperties.setName(name); + AdapterService service = getService(); + if (service == null) return false; + return service.setName(name); } public int getScanMode() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getScanMode(); + AdapterService service = getService(); + if (service == null) return BluetoothAdapter.SCAN_MODE_NONE; + return service.getScanMode(); } public boolean setScanMode(int mode, int duration) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - setDiscoverableTimeout(duration); - - int newMode = convertScanModeToHal(mode); - return mAdapterProperties.setScanMode(newMode); + AdapterService service = getService(); + if (service == null) return false; + return service.setScanMode(mode,duration); } public int getDiscoverableTimeout() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getDiscoverableTimeout(); + AdapterService service = getService(); + if (service == null) return 0; + return service.getDiscoverableTimeout(); } public boolean setDiscoverableTimeout(int timeout) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.setDiscoverableTimeout(timeout); + AdapterService service = getService(); + if (service == null) return false; + return service.setDiscoverableTimeout(timeout); } public boolean startDiscovery() { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - return startDiscoveryNative(); + AdapterService service = getService(); + if (service == null) return false; + return service.startDiscovery(); } public boolean cancelDiscovery() { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - return cancelDiscoveryNative(); + AdapterService service = getService(); + if (service == null) return false; + return service.cancelDiscovery(); } - public boolean isDiscovering() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.isDiscovering(); + AdapterService service = getService(); + if (service == null) return false; + return service.isDiscovering(); } public BluetoothDevice[] getBondedDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - debugLog("Get Bonded Devices being called"); - return mAdapterProperties.getBondedDevices(); + AdapterService service = getService(); + if (service == null) return new BluetoothDevice[0]; + return service.getBondedDevices(); } public int getAdapterConnectionState() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getConnectionState(); + AdapterService service = getService(); + if (service == null) return BluetoothAdapter.STATE_DISCONNECTED; + return service.getAdapterConnectionState(); } public int getProfileConnectionState(int profile) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mAdapterProperties.getProfileConnectionState(profile); + AdapterService service = getService(); + if (service == null) return BluetoothProfile.STATE_DISCONNECTED; + return service.getProfileConnectionState(profile); } public boolean createBond(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) { - return false; - } - - Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND); - msg.obj = device; - mBondStateMachine.sendMessage(msg); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.createBond(device); } public boolean cancelBondProcess(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); - byte[] addr = Utils.getBytesFromAddress(device.getAddress()); - return cancelBondNative(addr); + AdapterService service = getService(); + if (service == null) return false; + return service.cancelBondProcess(device); } public boolean removeBond(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDED) { - return false; - } - Message msg = mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND); - msg.obj = device; - mBondStateMachine.sendMessage(msg); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.removeBond(device); } public int getBondState(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) { - return BluetoothDevice.BOND_NONE; - } - return deviceProp.getBondState(); + AdapterService service = getService(); + if (service == null) return BluetoothDevice.BOND_NONE; + return service.getBondState(device); } public String getRemoteName(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) return null; - return deviceProp.getName(); + AdapterService service = getService(); + if (service == null) return null; + return service.getRemoteName(device); } public String getRemoteAlias(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) return null; - return deviceProp.getAlias(); + AdapterService service = getService(); + if (service == null) return null; + return service.getRemoteAlias(device); } public boolean setRemoteAlias(BluetoothDevice device, String name) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) return false; - deviceProp.setAlias(name); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.setRemoteAlias(device, name); } public int getRemoteClass(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) return 0; - - return deviceProp.getBluetoothClass(); + AdapterService service = getService(); + if (service == null) return 0; + return service.getRemoteClass(device); } public ParcelUuid[] getRemoteUuids(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null) return null; - return deviceProp.getUuids(); + AdapterService service = getService(); + if (service == null) return null; + return service.getRemoteUuids(device); } public boolean fetchRemoteUuids(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - mRemoteDevices.fetchUuids(device); - return true; + AdapterService service = getService(); + if (service == null) return false; + return service.fetchRemoteUuids(device); } public boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { - return false; - } - - byte[] addr = Utils.getBytesFromAddress(device.getAddress()); - return pinReplyNative(addr, accept, len, pinCode); + AdapterService service = getService(); + if (service == null) return false; + return service.setPin(device, accept, len, pinCode); } public boolean setPasskey(BluetoothDevice device, boolean accept, int len, byte[] passkey) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { - return false; - } - - byte[] addr = Utils.getBytesFromAddress(device.getAddress()); - return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY, accept, - Utils.byteArrayToInt(passkey)); + AdapterService service = getService(); + if (service == null) return false; + return service.setPasskey(device, accept, len, passkey); } public boolean setPairingConfirmation(BluetoothDevice device, boolean accept) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); - if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { - return false; - } - - byte[] addr = Utils.getBytesFromAddress(device.getAddress()); - return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION, - accept, 0); + AdapterService service = getService(); + if (service == null) return false; + return service.setPairingConfirmation(device, accept); } public void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) { - // TODO(BT) permission check? - // Since this is a binder call check if Bluetooth is on still - if (getState() == BluetoothAdapter.STATE_OFF) return; - - mAdapterProperties.sendConnectionStateChange(device, profile, state, prevState); - + AdapterService service = getService(); + if (service == null) return; + service.sendConnectionStateChange(device, profile, state, prevState); } public ParcelFileDescriptor connectSocket(BluetoothDevice device, int type, ParcelUuid uuid, int port, int flag) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - int fd = connectSocketNative(Utils.getBytesFromAddress(device.getAddress()), - type, Utils.uuidToByteArray(uuid), port, flag); - if (fd < 0) { - errorLog("Failed to connect socket"); - return null; - } - return ParcelFileDescriptor.adoptFd(fd); + AdapterService service = getService(); + if (service == null) return null; + return service.connectSocket(device, type, uuid, port, flag); } public ParcelFileDescriptor createSocketChannel(int type, String serviceName, ParcelUuid uuid, int port, int flag) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - int fd = createSocketChannelNative(type, serviceName, - Utils.uuidToByteArray(uuid), port, flag); - if (fd < 0) { - errorLog("Failed to create socket channel"); - return null; - } - return ParcelFileDescriptor.adoptFd(fd); + AdapterService service = getService(); + if (service == null) return null; + return service.createSocketChannel(type, serviceName, uuid, port, flag); } }; - private int convertScanModeToHal(int mode) { + + //----API Methods-------- + boolean isEnabled() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getState() == BluetoothAdapter.STATE_ON; + } + + int getState() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + debugLog("getState(): mAdapterProperties: " + mAdapterProperties); + return mAdapterProperties.getState(); + } + + boolean enable() { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + if (DBG) debugLog("enable() called..."); + Message m = + mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON); + m.arg1 = 1; //persist state + mAdapterStateMachine.sendMessage(m); + return true; + } + + boolean disable(boolean persist) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + if (DBG) debugLog("disable() called..."); + int val = (persist ? 1 : 0); + Message m = + mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF); + m.arg1 = val; + mAdapterStateMachine.sendMessage(m); + return true; + } + + String getAddress() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + String addrString = null; + byte[] address = mAdapterProperties.getAddress(); + return Utils.getAddressStringFromByte(address); + } + + ParcelUuid[] getUuids() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getUuids(); + } + + String getName() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + try { + return mAdapterProperties.getName(); + } catch (Throwable t) { + Log.d(TAG, "Unexpected exception while calling getName()",t); + } + return null; + } + + boolean setName(String name) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + return mAdapterProperties.setName(name); + } + + int getScanMode() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getScanMode(); + } + + boolean setScanMode(int mode, int duration) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + setDiscoverableTimeout(duration); + + int newMode = convertScanModeToHal(mode); + return mAdapterProperties.setScanMode(newMode); + } + + int getDiscoverableTimeout() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getDiscoverableTimeout(); + } + + boolean setDiscoverableTimeout(int timeout) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.setDiscoverableTimeout(timeout); + } + + boolean startDiscovery() { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + return startDiscoveryNative(); + } + + boolean cancelDiscovery() { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + return cancelDiscoveryNative(); + } + + boolean isDiscovering() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.isDiscovering(); + } + + BluetoothDevice[] getBondedDevices() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + debugLog("Get Bonded Devices being called"); + return mAdapterProperties.getBondedDevices(); + } + + int getAdapterConnectionState() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getConnectionState(); + } + + int getProfileConnectionState(int profile) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mAdapterProperties.getProfileConnectionState(profile); + } + + boolean createBond(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) { + return false; + } + + Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND); + msg.obj = device; + mBondStateMachine.sendMessage(msg); + return true; + } + + boolean cancelBondProcess(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); + byte[] addr = Utils.getBytesFromAddress(device.getAddress()); + return cancelBondNative(addr); + } + + boolean removeBond(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDED) { + return false; + } + Message msg = mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND); + msg.obj = device; + mBondStateMachine.sendMessage(msg); + return true; + } + + int getBondState(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) { + return BluetoothDevice.BOND_NONE; + } + return deviceProp.getBondState(); + } + + String getRemoteName(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) return null; + return deviceProp.getName(); + } + + String getRemoteAlias(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) return null; + return deviceProp.getAlias(); + } + + boolean setRemoteAlias(BluetoothDevice device, String name) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) return false; + deviceProp.setAlias(name); + return true; + } + + int getRemoteClass(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) return 0; + + return deviceProp.getBluetoothClass(); + } + + ParcelUuid[] getRemoteUuids(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null) return null; + return deviceProp.getUuids(); + } + + boolean fetchRemoteUuids(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + mRemoteDevices.fetchUuids(device); + return true; + } + + boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { + return false; + } + + byte[] addr = Utils.getBytesFromAddress(device.getAddress()); + return pinReplyNative(addr, accept, len, pinCode); + } + + boolean setPasskey(BluetoothDevice device, boolean accept, int len, byte[] passkey) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { + return false; + } + + byte[] addr = Utils.getBytesFromAddress(device.getAddress()); + return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY, accept, + Utils.byteArrayToInt(passkey)); + } + + boolean setPairingConfirmation(BluetoothDevice device, boolean accept) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); + if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { + return false; + } + + byte[] addr = Utils.getBytesFromAddress(device.getAddress()); + return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + accept, 0); + } + + void sendConnectionStateChange(BluetoothDevice + device, int profile, int state, int prevState) { + // TODO(BT) permission check? + // Since this is a binder call check if Bluetooth is on still + if (getState() == BluetoothAdapter.STATE_OFF) return; + + mAdapterProperties.sendConnectionStateChange(device, profile, state, prevState); + + } + + ParcelFileDescriptor connectSocket(BluetoothDevice device, int type, + ParcelUuid uuid, int port, int flag) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + int fd = connectSocketNative(Utils.getBytesFromAddress(device.getAddress()), + type, Utils.uuidToByteArray(uuid), port, flag); + if (fd < 0) { + errorLog("Failed to connect socket"); + return null; + } + return ParcelFileDescriptor.adoptFd(fd); + } + + ParcelFileDescriptor createSocketChannel(int type, String serviceName, + ParcelUuid uuid, int port, int flag) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + int fd = createSocketChannelNative(type, serviceName, + Utils.uuidToByteArray(uuid), port, flag); + if (fd < 0) { + errorLog("Failed to create socket channel"); + return null; + } + return ParcelFileDescriptor.adoptFd(fd); + } + + private static int convertScanModeToHal(int mode) { switch (mode) { case BluetoothAdapter.SCAN_MODE_NONE: return AbstractionLayer.BT_SCAN_MODE_NONE; @@ -774,11 +1021,11 @@ public class AdapterService extends Service { case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: return AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; } - errorLog("Incorrect scan mode in convertScanModeToHal"); + // errorLog("Incorrect scan mode in convertScanModeToHal"); return -1; } - int convertScanModeFromHal(int mode) { + static int convertScanModeFromHal(int mode) { switch (mode) { case AbstractionLayer.BT_SCAN_MODE_NONE: return BluetoothAdapter.SCAN_MODE_NONE; @@ -787,7 +1034,7 @@ public class AdapterService extends Service { case AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE: return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; } - errorLog("Incorrect scan mode in convertScanModeFromHal"); + //errorLog("Incorrect scan mode in convertScanModeFromHal"); return -1; } @@ -830,4 +1077,14 @@ public class AdapterService extends Service { byte[] uuid, int port, int flag); private native int createSocketChannelNative(int type, String serviceName, byte[] uuid, int port, int flag); + + protected void finalize() { + cleanup(); + if (TRACE_REF) { + synchronized (AdapterService.class) { + sRefCount--; + Log.d(TAG, "REFCOUNT: FINALIZED. INSTANCE_COUNT= " + sRefCount); + } + } + } } diff --git a/src/com/android/bluetooth/btservice/AdapterState.java b/src/com/android/bluetooth/btservice/AdapterState.java index f0b4300e6..9c6fc97bb 100755 --- a/src/com/android/bluetooth/btservice/AdapterState.java +++ b/src/com/android/bluetooth/btservice/AdapterState.java @@ -30,12 +30,11 @@ final class AdapterState extends StateMachine { static final int USER_TURN_ON = 1; static final int STARTED=2; static final int ENABLED_READY = 3; - // static final int POST_ENABLE =4; static final int USER_TURN_OFF = 20; static final int BEGIN_DISABLE = 21; static final int ALL_DEVICES_DISCONNECTED = 22; - // static final int DISABLE=23; + static final int DISABLED = 24; static final int STOPPED=25; @@ -53,7 +52,6 @@ final class AdapterState extends StateMachine { private static final int STOP_TIMEOUT_DELAY = 5000; private static final int PROPERTY_OP_DELAY =2000; private AdapterService mAdapterService; - private Context mContext; private AdapterProperties mAdapterProperties; private PendingCommandState mPendingCommandState = new PendingCommandState(); private OnState mOnState = new OnState(); @@ -61,24 +59,22 @@ final class AdapterState extends StateMachine { public boolean isTurningOn() { boolean isTurningOn= mPendingCommandState.isTurningOn(); - Log.d(TAG,"isTurningOn()=" + isTurningOn); + if (DBG) Log.d(TAG,"isTurningOn()=" + isTurningOn); return isTurningOn; } public boolean isTurningOff() { boolean isTurningOff= mPendingCommandState.isTurningOff(); - Log.d(TAG,"isTurningOff()=" + isTurningOff); + if (DBG) Log.d(TAG,"isTurningOff()=" + isTurningOff); return isTurningOff; } - public AdapterState(AdapterService service, Context context, - AdapterProperties adapterProperties) { + public AdapterState(AdapterService service,AdapterProperties adapterProperties) { super("BluetoothAdapterState:"); addState(mOnState); addState(mOffState); addState(mPendingCommandState); mAdapterService = service; - mContext = context; mAdapterProperties = adapterProperties; setInitialState(mOffState); } @@ -88,8 +84,6 @@ final class AdapterState extends StateMachine { mAdapterProperties = null; if(mAdapterService != null) mAdapterService = null; - if(mContext != null) - mContext = null; } private class OffState extends State { @@ -273,7 +267,7 @@ final class AdapterState extends StateMachine { Log.d(TAG,"Stopping profile services that were post enabled"); break; } - //Fall through if no post-enabled services or services already stopped + //Fall through if no services or services already stopped case STOPPED: if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = STOPPED, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); removeMessages(STOP_TIMEOUT); @@ -282,7 +276,6 @@ final class AdapterState extends StateMachine { setOffRequestId(-1); transitionTo(mOffState); sendIntent(BluetoothAdapter.STATE_OFF); - mAdapterService.processStopped(); mAdapterService.startShutdown(requestId); break; case START_TIMEOUT: @@ -328,7 +321,7 @@ final class AdapterState extends StateMachine { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mAdapterProperties.setState(newState); - mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); infoLog("Bluetooth State Change Intent: " + oldState + " -> " + newState); } diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java index 0bc53705d..70cfac683 100755 --- a/src/com/android/bluetooth/btservice/BondStateMachine.java +++ b/src/com/android/bluetooth/btservice/BondStateMachine.java @@ -40,14 +40,13 @@ final class BondStateMachine extends StateMachine { static final int BOND_STATE_BONDED = 2; private AdapterService mAdapterService; - private Context mContext; private AdapterProperties mAdapterProperties; private RemoteDevices mRemoteDevices; private PendingCommandState mPendingCommandState = new PendingCommandState(); private StableState mStableState = new StableState(); - public BondStateMachine(AdapterService service, Context context, + public BondStateMachine(AdapterService service, AdapterProperties prop, RemoteDevices remoteDevices) { super("BondStateMachine:"); addState(mStableState); @@ -55,11 +54,13 @@ final class BondStateMachine extends StateMachine { mRemoteDevices = remoteDevices; mAdapterService = service; mAdapterProperties = prop; - mContext = context; setInitialState(mStableState); } public void cleanup() { + mAdapterService = null; + mRemoteDevices = null; + mAdapterProperties = null; } private class StableState extends State { @@ -221,7 +222,7 @@ final class BondStateMachine extends StateMachine { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); - mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); infoLog("Bond State Change Intent:" + device + " OldState: " + oldState + " NewState: " + newState); } diff --git a/src/com/android/bluetooth/btservice/JniCallbacks.java b/src/com/android/bluetooth/btservice/JniCallbacks.java index e992d5e97..61b0741f2 100644 --- a/src/com/android/bluetooth/btservice/JniCallbacks.java +++ b/src/com/android/bluetooth/btservice/JniCallbacks.java @@ -6,43 +6,28 @@ package com.android.bluetooth.btservice; final class JniCallbacks { - private static JniCallbacks sInstance; private RemoteDevices mRemoteDevices; private AdapterProperties mAdapterProperties; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; - private JniCallbacks(RemoteDevices remoteDevices, AdapterProperties adapterProperties, - AdapterState adapterStateMachine, BondStateMachine bondStateMachine) { - mRemoteDevices = remoteDevices; - mAdapterProperties = adapterProperties; + JniCallbacks(AdapterState adapterStateMachine,AdapterProperties adapterProperties) { mAdapterStateMachine = adapterStateMachine; - mBondStateMachine = bondStateMachine; - } - - static synchronized JniCallbacks getInstance(RemoteDevices remoteDevices, - AdapterProperties adapterProperties, AdapterState adapterStateMachine, - BondStateMachine bondStateMachine) { - if (sInstance == null) { - sInstance = - new JniCallbacks(remoteDevices, adapterProperties, adapterStateMachine, - bondStateMachine); - } else { - sInstance.init(remoteDevices, adapterProperties, adapterStateMachine, - bondStateMachine); - } - return sInstance; + mAdapterProperties = adapterProperties; } - void init(RemoteDevices remoteDevices, - AdapterProperties adapterProperties, AdapterState adapterStateMachine, - BondStateMachine bondStateMachine) { + void init(BondStateMachine bondStateMachine, RemoteDevices remoteDevices) { mRemoteDevices = remoteDevices; - mAdapterProperties = adapterProperties; - mAdapterStateMachine = adapterStateMachine; mBondStateMachine = bondStateMachine; } + void cleanup() { + mRemoteDevices = null; + mAdapterProperties = null; + mAdapterStateMachine = null; + mBondStateMachine = null; + } + public Object Clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } diff --git a/src/com/android/bluetooth/btservice/ProfileService.java b/src/com/android/bluetooth/btservice/ProfileService.java index 64b58d3be..baaf40397 100644 --- a/src/com/android/bluetooth/btservice/ProfileService.java +++ b/src/com/android/bluetooth/btservice/ProfileService.java @@ -1,60 +1,94 @@ package com.android.bluetooth.btservice; +import java.util.HashMap; + +import com.android.bluetooth.Utils; + import android.app.Service; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.IBinder; import android.util.Log; public abstract class ProfileService extends Service { + private static final boolean DBG = true; + //For Debugging only + private static HashMap<String, Integer> sReferenceCount = new HashMap<String,Integer>(); + public static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; public static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + public static interface IProfileServiceBinder extends IBinder { + public boolean cleanup(); + } //Profile services will not be automatically restarted. //They must be explicitly restarted by AdapterService private static final int PROFILE_SERVICE_MODE=Service.START_NOT_STICKY; - - protected BluetoothAdapter mAdapter; protected String mName; + protected BluetoothAdapter mAdapter; + protected IProfileServiceBinder mBinder; + protected boolean mStartError=false; + private boolean mCleaningUp = false; protected String getName() { return getClass().getSimpleName(); } - public abstract IBinder onBind(Intent intent); + protected boolean isAvailable() { + return !mStartError && !mCleaningUp; + } + + protected abstract IProfileServiceBinder initBinder(); protected abstract boolean start(); protected abstract boolean stop(); + protected boolean cleanup() { + return true; + } - public boolean mStartError=false; - @Override - public void onCreate() { + protected ProfileService() { mName = getName(); - if (mName == null) { - mName = "UnknownProfileService"; + if (DBG) { + synchronized (sReferenceCount) { + Integer refCount = sReferenceCount.get(mName); + if (refCount==null) { + refCount = 1; + } else { + refCount = refCount+1; + } + sReferenceCount.put(mName, refCount); + log("REFCOUNT: CREATED. INSTANCE_COUNT=" +refCount); + } } - mAdapter = BluetoothAdapter.getDefaultAdapter(); - - log("onCreate"); - super.onCreate(); } - private void doStart(Intent intent) { - - //Start service - if (mAdapter == null) { - Log.e(mName, "Error starting profile. BluetoothAdapter is null"); - } else { - mStartError = !start(); - if (!mStartError) { - notifyProfileServiceStateChange(BluetoothAdapter.STATE_ON); - } else { - Log.e(mName, "Error starting profile. BluetoothAdapter is null"); + protected void finalize() { + if (DBG) { + synchronized (sReferenceCount) { + Integer refCount = sReferenceCount.get(mName); + if (refCount!=null) { + refCount = refCount-1; + } else { + refCount = 0; + } + sReferenceCount.put(mName, refCount); + log("REFCOUNT: FINALIZED. INSTANCE_COUNT=" +refCount); } } } + @Override + public void onCreate() { + log("onCreate"); + super.onCreate(); + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mBinder = initBinder(); + } + public int onStartCommand(Intent intent, int flags, int startId) { log("onStartCommand()"); if (mStartError || mAdapter == null) { @@ -87,22 +121,60 @@ public abstract class ProfileService extends Service { return PROFILE_SERVICE_MODE; } + public IBinder onBind(Intent intent) { + log("onBind"); + return mBinder; + } + + public boolean onUnbind(Intent intent) { + log("onUnbind"); + return super.onUnbind(intent); + } + @Override public void onDestroy() { - super.onDestroy(); log("Destroying service."); + if (mCleaningUp) { + log("Cleanup already started... Skipping cleanup()..."); + } else { + log("cleanup()"); + mCleaningUp = true; + cleanup(); + if (mBinder != null) { + mBinder.cleanup(); + mBinder= null; + } + } + super.onDestroy(); + mAdapter = null; + } + + private void doStart(Intent intent) { + //Start service + if (mAdapter == null) { + Log.e(mName, "Error starting profile. BluetoothAdapter is null"); + } else { + log("start()"); + mStartError = !start(); + if (!mStartError) { + notifyProfileServiceStateChanged(BluetoothAdapter.STATE_ON); + } else { + Log.e(mName, "Error starting profile. BluetoothAdapter is null"); + } + } } private void doStop(Intent intent) { if (stop()) { - notifyProfileServiceStateChange(BluetoothAdapter.STATE_OFF); + log("stop()"); + notifyProfileServiceStateChanged(BluetoothAdapter.STATE_OFF); stopSelf(); } else { Log.e(mName, "Unable to stop profile"); } } - protected void notifyProfileServiceStateChange(int state) { + protected void notifyProfileServiceStateChanged(int state) { //Notify adapter service AdapterService sAdapter = AdapterService.getAdapterService(); if (sAdapter!= null) { @@ -110,6 +182,17 @@ public abstract class ProfileService extends Service { } } + public void notifyProfileConnectionStateChanged(BluetoothDevice device, + int profileId, int newState, int prevState) { + AdapterService svc = AdapterService.getAdapterService(); + if (svc != null) { + svc.onProfileConnectionStateChanged(device, profileId, newState, prevState); + } + } + + protected BluetoothDevice getDevice(byte[] address) { + return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); + } protected void log(String msg) { Log.d(mName, msg); diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java index 21201f0a3..08ef48aba 100755 --- a/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -26,7 +26,7 @@ final class RemoteDevices { private static final boolean DBG = true; private static final String TAG = "BluetoothRemoteDevices"; - private static Context mContext; + private static BluetoothAdapter mAdapter; private static AdapterService mAdapterService; private static ArrayList<BluetoothDevice> mSdpTracker; @@ -37,28 +37,21 @@ final class RemoteDevices { private static final int MESSAGE_UUID_INTENT = 1; private HashMap<BluetoothDevice, DeviceProperties> mDevices; - private static RemoteDevices sInstance; - RemoteDevices(AdapterService service, Context context) { + RemoteDevices(AdapterService service) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - mContext = context; mAdapterService = service; mSdpTracker = new ArrayList<BluetoothDevice>(); mDevices = new HashMap<BluetoothDevice, DeviceProperties>(); } - public void init() { - mSdpTracker.clear(); - mDevices.clear(); - } - public void cleanup() { + void cleanup() { mSdpTracker.clear(); mSdpTracker = null; mDevices.clear(); mDevices = null; mAdapterService = null; - mContext = null; mAdapter= null; } @@ -204,7 +197,7 @@ final class RemoteDevices { Intent intent = new Intent(BluetoothDevice.ACTION_UUID); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null? null: prop.mUuids); - mContext.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM); + mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM); //Remove the outstanding UUID request mSdpTracker.remove(device); @@ -216,7 +209,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin); intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); } void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { @@ -243,7 +236,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); debugLog("Remote Device name is: " + device.mName); break; case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: @@ -263,7 +256,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_CLASS, new BluetoothClass(device.mBluetoothClass)); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); debugLog("Remote class is:" + device.mBluetoothClass); break; case AbstractionLayer.BT_PROPERTY_UUIDS: @@ -300,7 +293,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi); intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); } void pinRequestCallback(byte[] address, byte[] name, int cod) { @@ -333,7 +326,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); return; } @@ -375,7 +368,7 @@ final class RemoteDevices { intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, passkey); } intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM); } void aclStateChangeCallback(int status, byte[] address, int newState) { @@ -396,7 +389,7 @@ final class RemoteDevices { } intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); + mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM); } void fetchUuids(BluetoothDevice device) { @@ -440,4 +433,5 @@ final class RemoteDevices { private void warnLog(String msg) { Log.w(TAG, msg); } + } diff --git a/src/com/android/bluetooth/hdp/HealthService.java b/src/com/android/bluetooth/hdp/HealthService.java index 458deb2d3..085fcc8c1 100755 --- a/src/com/android/bluetooth/hdp/HealthService.java +++ b/src/com/android/bluetooth/hdp/HealthService.java @@ -4,8 +4,6 @@ package com.android.bluetooth.hdp; -import android.app.Service; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealth; import android.bluetooth.BluetoothHealthAppConfiguration; @@ -14,7 +12,6 @@ import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothHealth; import android.bluetooth.IBluetoothHealthCallback; import android.content.Intent; -import android.os.IBinder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -33,8 +30,8 @@ import java.util.Map; import java.util.Map.Entry; import com.android.bluetooth.Utils; import android.content.pm.PackageManager; -import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; /** * Provides Bluetooth Health Device profile, as a service in @@ -44,9 +41,11 @@ import com.android.bluetooth.btservice.ProfileService; public class HealthService extends ProfileService { private static final boolean DBG = true; private static final String TAG="HealthService"; + private List<HealthChannel> mHealthChannels; private Map <BluetoothHealthAppConfiguration, AppInfo> mApps; private Map <BluetoothDevice, Integer> mHealthDevices; + private boolean mNativeAvailable; private HealthServiceMessageHandler mHandler; private static final int MESSAGE_REGISTER_APPLICATION = 1; private static final int MESSAGE_UNREGISTER_APPLICATION = 2; @@ -63,29 +62,11 @@ public class HealthService extends ProfileService { return TAG; } - @Override - public IBinder onBind(Intent intent) { - log("onBind"); - return mBinder; + protected IProfileServiceBinder initBinder() { + return new BluetoothHealthBinder(this); } protected boolean start() { - - //Cleanup other object references - if(mHealthChannels != null) { - mHealthChannels.clear(); - mHealthChannels = null; - } - if(mHealthDevices != null) { - mHealthDevices.clear(); - mHealthDevices = null; - } - if(mApps != null) { - mApps.clear(); - mApps = null; - } - - if (DBG) log("startService"); mHealthChannels = Collections.synchronizedList(new ArrayList<HealthChannel>()); mApps = Collections.synchronizedMap(new HashMap<BluetoothHealthAppConfiguration, AppInfo>()); @@ -96,24 +77,26 @@ public class HealthService extends ProfileService { Looper looper = thread.getLooper(); mHandler = new HealthServiceMessageHandler(looper); initializeNative(); - + mNativeAvailable=true; return true; } protected boolean stop() { - if (DBG) log("stop()"); - - //Cleanup looper mHandler.removeCallbacksAndMessages(null); Looper looper = mHandler.getLooper(); if (looper != null) { looper.quit(); } - mHandler = null; + return true; + } + protected boolean cleanup() { + mHandler = null; //Cleanup native - cleanupNative(); - + if (mNativeAvailable) { + cleanupNative(); + mNativeAvailable=false; + } if(mHealthChannels != null) { mHealthChannels.clear(); mHealthChannels = null; @@ -175,7 +158,7 @@ public class HealthService extends ProfileService { case MESSAGE_CONNECT_CHANNEL: { HealthChannel chan = (HealthChannel) msg.obj; - byte[] devAddr = getByteAddress(chan.mDevice); + byte[] devAddr = Utils.getByteAddress(chan.mDevice); int appId = (mApps.get(chan.mConfig)).mAppId; chan.mChannelId = connectChannelNative(devAddr, appId); if (chan.mChannelId == -1) { @@ -256,96 +239,170 @@ public class HealthService extends ProfileService { /** * Handlers for incoming service calls */ - private final IBluetoothHealth.Stub mBinder = new IBluetoothHealth.Stub() { - public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, - IBluetoothHealthCallback callback) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, - "Need BLUETOOTH permission"); - if (mApps.get(config) != null) { - if (DBG) log("Config has already been registered"); - return false; - } - mApps.put(config, new AppInfo(callback)); - Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION); - msg.obj = config; - mHandler.sendMessage(msg); + private static class BluetoothHealthBinder extends IBluetoothHealth.Stub implements IProfileServiceBinder { + private HealthService mService; + + public BluetoothHealthBinder(HealthService svc) { + mService = svc; + } + + public boolean cleanup() { + mService = null; return true; } - public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (mApps.get(config) == null) { - if (DBG) log("unregisterAppConfiguration: no app found"); - return false; + private HealthService getService() { + if (mService != null && mService.isAvailable()) { + return mService; } + return null; + } - Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_APPLICATION); - msg.obj = config; - mHandler.sendMessage(msg); - return true; + public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { + HealthService service = getService(); + if (service == null) return false; + return service.registerAppConfiguration(config, callback); + } + + public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { + HealthService service = getService(); + if (service == null) return false; + return service.unregisterAppConfiguration(config); } public boolean connectChannelToSource(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return connectChannel(device, config, BluetoothHealth.CHANNEL_TYPE_ANY); + HealthService service = getService(); + if (service == null) return false; + return service.connectChannelToSource(device, config); } public boolean connectChannelToSink(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelType) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return connectChannel(device, config, channelType); + HealthService service = getService(); + if (service == null) return false; + return service.connectChannelToSink(device, config, channelType); } public boolean disconnectChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config, int channelId) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - HealthChannel chan = findChannelById(channelId); - if (chan == null) { - if (DBG) log("disconnectChannel: no channel found"); - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT_CHANNEL); - msg.obj = chan; - mHandler.sendMessage(msg); - return true; + HealthService service = getService(); + if (service == null) return false; + return service.disconnectChannel(device, config, channelId); } public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - HealthChannel healthChan = null; - for (HealthChannel chan: mHealthChannels) { - if (chan.mDevice.equals(device) && chan.mConfig.equals(config)) { - healthChan = chan; - } - } - if (healthChan == null) { - Log.e(TAG, "No channel found for device: " + device + " config: " + config); - return null; - } - return healthChan.mChannelFd; + HealthService service = getService(); + if (service == null) return null; + return service.getMainChannelFd(device, config); } public int getHealthDeviceConnectionState(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getConnectionState(device); + HealthService service = getService(); + if (service == null) return BluetoothHealth.STATE_DISCONNECTED; + return service.getHealthDeviceConnectionState(device); } public List<BluetoothDevice> getConnectedHealthDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates( - new int[] {BluetoothHealth.STATE_CONNECTED}); - return devices; + HealthService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice> (0); + return service.getConnectedHealthDevices(); } public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(states); - return devices; + HealthService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice> (0); + return service.getHealthDevicesMatchingConnectionStates(states); } }; + boolean registerAppConfiguration(BluetoothHealthAppConfiguration config, + IBluetoothHealthCallback callback) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, + "Need BLUETOOTH permission"); + if (mApps.get(config) != null) { + if (DBG) Log.d(TAG, "Config has already been registered"); + return false; + } + mApps.put(config, new AppInfo(callback)); + Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION,config); + mHandler.sendMessage(msg); + return true; + } + + boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (mApps.get(config) == null) { + if (DBG) Log.d(TAG,"unregisterAppConfiguration: no app found"); + return false; + } + + Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_APPLICATION,config); + mHandler.sendMessage(msg); + return true; + } + + boolean connectChannelToSource(BluetoothDevice device, + BluetoothHealthAppConfiguration config) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return connectChannel(device, config, BluetoothHealth.CHANNEL_TYPE_ANY); + } + + boolean connectChannelToSink(BluetoothDevice device, + BluetoothHealthAppConfiguration config, int channelType) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return connectChannel(device, config, channelType); + } + + boolean disconnectChannel(BluetoothDevice device, + BluetoothHealthAppConfiguration config, int channelId) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + HealthChannel chan = findChannelById(channelId); + if (chan == null) { + if (DBG) Log.d(TAG,"disconnectChannel: no channel found"); + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT_CHANNEL,chan); + mHandler.sendMessage(msg); + return true; + } + + ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, + BluetoothHealthAppConfiguration config) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + HealthChannel healthChan = null; + for (HealthChannel chan: mHealthChannels) { + if (chan.mDevice.equals(device) && chan.mConfig.equals(config)) { + healthChan = chan; + } + } + if (healthChan == null) { + Log.e(TAG, "No channel found for device: " + device + " config: " + config); + return null; + } + return healthChan.mChannelFd; + } + + int getHealthDeviceConnectionState(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return getConnectionState(device); + } + + List<BluetoothDevice> getConnectedHealthDevices() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates( + new int[] {BluetoothHealth.STATE_CONNECTED}); + return devices; + } + + List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(states); + return devices; + } + private void onAppRegistrationState(int appId, int state) { Message msg = mHandler.obtainMessage(MESSAGE_APP_REGISTRATION_CALLBACK); msg.arg1 = appId; @@ -568,10 +625,7 @@ public class HealthService extends ProfileService { } else { mHealthDevices.put(device, newDeviceState); } - AdapterService svc = AdapterService.getAdapterService(); - if (svc != null) { - svc.onProfileConnectionStateChanged(device, BluetoothProfile.HEALTH, newDeviceState, prevDeviceState); - } + notifyProfileConnectionStateChanged(device, BluetoothProfile.HEALTH, newDeviceState, prevDeviceState); } /** @@ -632,14 +686,6 @@ public class HealthService extends ProfileService { return channels; } - private BluetoothDevice getDevice(byte[] address) { - return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); - } - - private byte[] getByteAddress(BluetoothDevice device) { - return Utils.getBytesFromAddress(device.getAddress()); - } - private int getConnectionState(BluetoothDevice device) { if (mHealthDevices.get(device) == null) { return BluetoothHealth.STATE_DISCONNECTED; @@ -662,7 +708,7 @@ public class HealthService extends ProfileService { return healthDevices; } - private class AppInfo { + private static class AppInfo { private IBluetoothHealthCallback mCallback; private int mAppId; @@ -742,4 +788,5 @@ public class HealthService extends ProfileService { private native boolean unregisterHealthAppNative(int appId); private native int connectChannelNative(byte[] btAddress, int appId); private native boolean disconnectChannelNative(int channelId); + } diff --git a/src/com/android/bluetooth/hfp/AtPhonebook.java b/src/com/android/bluetooth/hfp/AtPhonebook.java index ef76dd773..dea20100f 100755 --- a/src/com/android/bluetooth/hfp/AtPhonebook.java +++ b/src/com/android/bluetooth/hfp/AtPhonebook.java @@ -21,6 +21,7 @@ import com.android.bluetooth.R; import com.android.internal.telephony.GsmAlphabet; import android.bluetooth.BluetoothDevice; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -72,10 +73,9 @@ public class AtPhonebook { public int nameColumn; }; + private Context mContext; + private ContentResolver mContentResolver; private HeadsetStateMachine mStateMachine; - - private final Context mContext; - private String mCurrentPhonebook; private String mCharacterSet = "UTF-8"; @@ -98,6 +98,7 @@ public class AtPhonebook { public AtPhonebook(Context context, HeadsetStateMachine headsetState) { mContext = context; + mContentResolver = context.getContentResolver(); mStateMachine = headsetState; mPhonebooks.put("DC", new PhonebookResult()); // dialled calls mPhonebooks.put("RC", new PhonebookResult()); // received calls @@ -110,10 +111,17 @@ public class AtPhonebook { mCheckingAccessPermission = false; } + public void cleanup() { + mPhonebooks.clear(); + mContentResolver = null; + mContext = null; + mStateMachine = null; + } + /** Returns the last dialled number, or null if no numbers have been called */ public String getLastDialledNumber() { String[] projection = {Calls.NUMBER}; - Cursor cursor = mContext.getContentResolver().query(Calls.CONTENT_URI, projection, + Cursor cursor = mContentResolver.query(Calls.CONTENT_URI, projection, Calls.TYPE + "=" + Calls.OUTGOING_TYPE, null, Calls.DEFAULT_SORT_ORDER + " LIMIT 1"); if (cursor == null) return null; @@ -387,7 +395,7 @@ public class AtPhonebook { } if (ancillaryPhonebook) { - pbr.cursor = mContext.getContentResolver().query( + pbr.cursor = mContentResolver.query( Calls.CONTENT_URI, CALLS_PROJECTION, where, null, Calls.DEFAULT_SORT_ORDER + " LIMIT " + MAX_PHONEBOOK_SIZE); if (pbr.cursor == null) return false; @@ -396,7 +404,7 @@ public class AtPhonebook { pbr.typeColumn = -1; pbr.nameColumn = -1; } else { - pbr.cursor = mContext.getContentResolver().query(Phone.CONTENT_URI, PHONES_PROJECTION, + pbr.cursor = mContentResolver.query(Phone.CONTENT_URI, PHONES_PROJECTION, where, null, Phone.NUMBER + " LIMIT " + MAX_PHONEBOOK_SIZE); if (pbr.cursor == null) return false; @@ -482,7 +490,7 @@ public class AtPhonebook { // try caller id lookup // TODO: This code is horribly inefficient. I saw it // take 7 seconds to process 100 missed calls. - Cursor c = mContext.getContentResolver(). + Cursor c = mContentResolver. query(Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE}, null, null, null); diff --git a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java index f549a9a89..874489670 100644 --- a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java +++ b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java @@ -19,7 +19,6 @@ import com.android.internal.telephony.Call; class HeadsetPhoneState { private static final String TAG = "HeadsetPhoneState"; - private Context mContext; private HeadsetStateMachine mStateMachine; private TelephonyManager mTelephonyManager; private ServiceState mServiceState; @@ -52,9 +51,14 @@ class HeadsetPhoneState { private boolean mListening = false; HeadsetPhoneState(Context context, HeadsetStateMachine stateMachine) { - mContext = context; mStateMachine = stateMachine; - mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + } + + public void cleanup() { + listenForPhoneState(false); + mTelephonyManager = null; + mStateMachine = null; } void listenForPhoneState(boolean start) { @@ -116,8 +120,11 @@ class HeadsetPhoneState { void setBatteryCharge(int batteryLevel) { if (mBatteryCharge != batteryLevel) { mBatteryCharge = batteryLevel; - mStateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, + HeadsetStateMachine sm = mStateMachine; + if (sm != null) { + sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, new HeadsetDeviceState(mService, mRoam, mSignal, mBatteryCharge)); + } } } @@ -145,7 +152,6 @@ class HeadsetPhoneState { return (mNumActive >= 1); } - private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onServiceStateChanged(ServiceState serviceState) { @@ -153,8 +159,11 @@ class HeadsetPhoneState { mService = (serviceState.getState() == ServiceState.STATE_IN_SERVICE) ? HeadsetHalConstants.NETWORK_STATE_AVAILABLE : HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE; - mStateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, + HeadsetStateMachine sm = mStateMachine; + if (sm != null) { + sm.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, new HeadsetDeviceState(mService, mRoam, mSignal, mBatteryCharge)); + } } @Override diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java index 817f05379..1bf9ee9aa 100755 --- a/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/src/com/android/bluetooth/hfp/HeadsetService.java @@ -4,9 +4,8 @@ package com.android.bluetooth.hfp; -import android.app.Service; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothHeadset; import android.content.BroadcastReceiver; @@ -14,17 +13,16 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; -import android.os.AsyncResult; import android.os.Handler; -import android.os.IBinder; import android.os.Message; import android.provider.Settings; import android.util.Log; + +import java.util.ArrayList; import java.util.List; import java.util.Iterator; import java.util.Map; import android.content.pm.PackageManager; -import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; /** @@ -35,18 +33,14 @@ import com.android.bluetooth.btservice.ProfileService; public class HeadsetService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "HeadsetService"; - private HeadsetStateMachine mStateMachine; - private boolean mReceiverRegistered; protected String getName() { return TAG; } - @Override - public IBinder onBind(Intent intent) { - log("onBind"); - return mBinder; + public IProfileServiceBinder initBinder() { + return new BluetoothHeadsetBinder(this); } protected boolean start() { @@ -55,25 +49,26 @@ public class HeadsetService extends ProfileService { IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); - - registerReceiver(mHeadsetReceiver, filter); - mReceiverRegistered=true; + try { + registerReceiver(mHeadsetReceiver, filter); + } catch (Exception e) { + Log.w(TAG,"Unable to register headset receiver",e); + } return true; } protected boolean stop() { - if (DBG) log("stopService()"); - if (mReceiverRegistered) { - try { - unregisterReceiver(mHeadsetReceiver); - } catch (Exception e) { - Log.w(TAG,"Unable to unregister headset receiver",e); - } - mReceiverRegistered = false; + try { + unregisterReceiver(mHeadsetReceiver); + } catch (Exception e) { + Log.w(TAG,"Unable to unregister headset receiver",e); } + mStateMachine.quit(); + return true; + } - if (mStateMachine!= null) { - mStateMachine.quit(); + protected boolean cleanup() { + if (mStateMachine != null) { mStateMachine.cleanup(); mStateMachine=null; } @@ -84,7 +79,6 @@ public class HeadsetService extends ProfileService { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent); } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { @@ -104,178 +98,329 @@ public class HeadsetService extends ProfileService { /** * Handlers for incoming service calls */ - private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() { - - public boolean connect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); + private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder { + private HeadsetService mService; - if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { - return false; - } + public BluetoothHeadsetBinder(HeadsetService svc) { + mService = svc; + } + public boolean cleanup() { + mService = null; + return true; + } - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState == BluetoothProfile.STATE_CONNECTED || - connectionState == BluetoothProfile.STATE_CONNECTING) { - return false; + private HeadsetService getService() { + if (mService != null && mService.isAvailable()) { + return mService; } + return null; + } - mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); - return true; + public boolean connect(BluetoothDevice device) { + HeadsetService service = getService(); + if (service == null) return false; + return service.connect(device); } public boolean disconnect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState != BluetoothProfile.STATE_CONNECTED && - connectionState != BluetoothProfile.STATE_CONNECTING) { - return false; - } - - mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.disconnect(device); } public List<BluetoothDevice> getConnectedDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getConnectedDevices(); + HeadsetService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getConnectedDevices(); } public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getDevicesMatchingConnectionStates(states); + HeadsetService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getDevicesMatchingConnectionStates(states); } public int getConnectionState(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.getConnectionState(device); + HeadsetService service = getService(); + if (service == null) return BluetoothProfile.STATE_DISCONNECTED; + return service.getConnectionState(device); } public boolean setPriority(BluetoothDevice device, int priority) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - Settings.Secure.putInt(getContentResolver(), - Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), - priority); - if (DBG) log("Saved priority " + device + " = " + priority); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.setPriority(device, priority); } public int getPriority(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int priority = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), - BluetoothProfile.PRIORITY_UNDEFINED); - return priority; + HeadsetService service = getService(); + if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; + return service.getPriority(device); } public boolean startVoiceRecognition(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState != BluetoothProfile.STATE_CONNECTED && - connectionState != BluetoothProfile.STATE_CONNECTING) { - return false; - } - mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.startVoiceRecognition(device); } public boolean stopVoiceRecognition(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - // It seem that we really need to check the AudioOn state. - // But since we allow startVoiceRecognition in STATE_CONNECTED and - // STATE_CONNECTING state, we do these 2 in this method - int connectionState = mStateMachine.getConnectionState(device); - if (connectionState != BluetoothProfile.STATE_CONNECTED && - connectionState != BluetoothProfile.STATE_CONNECTING) { - return false; - } - mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP); - // TODO is this return correct when the voice recognition is not on? - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.stopVoiceRecognition(device); } public boolean isAudioOn() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.isAudioOn(); + HeadsetService service = getService(); + if (service == null) return false; + return service.isAudioOn(); } public boolean isAudioConnected(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mStateMachine.isAudioConnected(device); + HeadsetService service = getService(); + if (service == null) return false; + return service.isAudioConnected(device); } public int getBatteryUsageHint(BluetoothDevice device) { - // TODO(BT) ask for BT stack support? - return 0; + HeadsetService service = getService(); + if (service == null) return 0; + return service.getBatteryUsageHint(device); } public boolean acceptIncomingConnect(BluetoothDevice device) { - // TODO(BT) remove it if stack does access control - return false; + HeadsetService service = getService(); + if (service == null) return false; + return service.acceptIncomingConnect(device); } public boolean rejectIncomingConnect(BluetoothDevice device) { - // TODO(BT) remove it if stack does access control - return false; + HeadsetService service = getService(); + if (service == null) return false; + return service.rejectIncomingConnect(device); } public int getAudioState(BluetoothDevice device) { - return mStateMachine.getAudioState(device); + HeadsetService service = getService(); + if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + return service.getAudioState(device); } public boolean connectAudio() { - // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!mStateMachine.isConnected()) { - return false; - } - if (mStateMachine.isAudioOn()) { - return false; - } - mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.connectAudio(); } public boolean disconnectAudio() { - // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!mStateMachine.isAudioOn()) { - return false; - } - mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.disconnectAudio(); } public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { - // TODO(BT) Is this right? - mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.startScoUsingVirtualVoiceCall(device); } public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { - // TODO(BT) Is this right? - mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); - return true; + HeadsetService service = getService(); + if (service == null) return false; + return service.stopScoUsingVirtualVoiceCall(device); } public void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type) { - mStateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, - new HeadsetCallState(numActive, numHeld, callState, number, type)); + HeadsetService service = getService(); + if (service == null) return; + service.phoneStateChanged(numActive, numHeld, callState, number, type); } public void roamChanged(boolean roam) { - mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam); + HeadsetService service = getService(); + if (service == null) return; + service.roamChanged(roam); } public void clccResponse(int index, int direction, int status, int mode, boolean mpty, String number, int type) { - mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, - new HeadsetClccResponse(index, direction, status, mode, mpty, number, type)); + HeadsetService service = getService(); + if (service == null) return; + service.clccResponse(index, direction, status, mode, mpty, number, type); } }; + //API methods + boolean connect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { + return false; + } + + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState == BluetoothProfile.STATE_CONNECTED || + connectionState == BluetoothProfile.STATE_CONNECTING) { + return false; + } + + mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); + return true; + } + + boolean disconnect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState != BluetoothProfile.STATE_CONNECTED && + connectionState != BluetoothProfile.STATE_CONNECTING) { + return false; + } + + mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); + return true; + } + + List<BluetoothDevice> getConnectedDevices() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getConnectedDevices(); + } + + private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getDevicesMatchingConnectionStates(states); + } + + int getConnectionState(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.getConnectionState(device); + } + + boolean setPriority(BluetoothDevice device, int priority) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), + priority); + if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority); + return true; + } + + int getPriority(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int priority = Settings.Secure.getInt(getContentResolver(), + Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), + BluetoothProfile.PRIORITY_UNDEFINED); + return priority; + } + + boolean startVoiceRecognition(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState != BluetoothProfile.STATE_CONNECTED && + connectionState != BluetoothProfile.STATE_CONNECTING) { + return false; + } + mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START); + return true; + } + + boolean stopVoiceRecognition(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + // It seem that we really need to check the AudioOn state. + // But since we allow startVoiceRecognition in STATE_CONNECTED and + // STATE_CONNECTING state, we do these 2 in this method + int connectionState = mStateMachine.getConnectionState(device); + if (connectionState != BluetoothProfile.STATE_CONNECTED && + connectionState != BluetoothProfile.STATE_CONNECTING) { + return false; + } + mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP); + // TODO is this return correct when the voice recognition is not on? + return true; + } + + boolean isAudioOn() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.isAudioOn(); + } + + boolean isAudioConnected(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mStateMachine.isAudioConnected(device); + } + + int getBatteryUsageHint(BluetoothDevice device) { + // TODO(BT) ask for BT stack support? + return 0; + } + + boolean acceptIncomingConnect(BluetoothDevice device) { + // TODO(BT) remove it if stack does access control + return false; + } + + boolean rejectIncomingConnect(BluetoothDevice device) { + // TODO(BT) remove it if stack does access control + return false; + } + + int getAudioState(BluetoothDevice device) { + return mStateMachine.getAudioState(device); + } + + boolean connectAudio() { + // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!mStateMachine.isConnected()) { + return false; + } + if (mStateMachine.isAudioOn()) { + return false; + } + mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO); + return true; + } + + boolean disconnectAudio() { + // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (!mStateMachine.isAudioOn()) { + return false; + } + mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO); + return true; + } + + boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { + // TODO(BT) Is this right? + mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); + return true; + } + + boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { + // TODO(BT) Is this right? + mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); + return true; + } + + private void phoneStateChanged(int numActive, int numHeld, int callState, + String number, int type) { + mStateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, + new HeadsetCallState(numActive, numHeld, callState, number, type)); + } + + private void roamChanged(boolean roam) { + mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam); + } + + private void clccResponse(int index, int direction, int status, int mode, boolean mpty, + String number, int type) { + mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, + new HeadsetClccResponse(index, direction, status, mode, mpty, number, type)); + } + } diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index 94452fa2e..974808b9e 100755 --- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -55,6 +55,8 @@ import java.util.Set; final class HeadsetStateMachine extends StateMachine { private static final String TAG = "HeadsetStateMachine"; private static final boolean DBG = true; + //For Debugging only + private static int sRefCount=0; private static final String HEADSET_NAME = "bt_headset_name"; private static final String HEADSET_NREC = "bt_headset_nrec"; @@ -95,9 +97,8 @@ final class HeadsetStateMachine extends StateMachine { private Connected mConnected; private AudioOn mAudioOn; - private Context mContext; + private HeadsetService mService; private PowerManager mPowerManager; - private boolean mVoiceRecognitionStarted = false; private boolean mWaitingForVoiceRecognition = false; private WakeLock mStartVoiceRecognitionWakeLock; // held while waiting for voice recognition @@ -112,6 +113,7 @@ final class HeadsetStateMachine extends StateMachine { private int mAudioState; private BluetoothAdapter mAdapter; private IBluetoothHeadsetPhone mPhoneProxy; + private boolean mNativeAvailable; // mCurrentDevice is the device connected before the state changes // mTargetDevice is the device to be connected @@ -143,10 +145,9 @@ final class HeadsetStateMachine extends StateMachine { classInitNative(); } - HeadsetStateMachine(Context context) { + HeadsetStateMachine(HeadsetService context) { super(TAG); - - mContext = context; + mService = context; mVoiceRecognitionStarted = false; mWaitingForVoiceRecognition = false; @@ -157,7 +158,7 @@ final class HeadsetStateMachine extends StateMachine { mDialingOut = false; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - mPhonebook = new AtPhonebook(mContext, this); + mPhonebook = new AtPhonebook(mService, this); mPhoneState = new HeadsetPhoneState(context, this); mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -167,6 +168,7 @@ final class HeadsetStateMachine extends StateMachine { } initializeNative(); + mNativeAvailable=true; mDisconnected = new Disconnected(); mPending = new Pending(); @@ -187,20 +189,32 @@ final class HeadsetStateMachine extends StateMachine { } public void cleanup() { - cleanupNative(); if (mPhoneProxy != null) { if (DBG) Log.d(TAG,"Unbinding service..."); synchronized (mConnection) { try { mPhoneProxy = null; - mContext.unbindService(mConnection); + mService.unbindService(mConnection); } catch (Exception re) { - Log.e(TAG,"",re); + Log.e(TAG,"Error unbinding from IBluetoothHeadsetPhone",re); } + } } + if (mPhoneState != null) { + mPhoneState.listenForPhoneState(false); + mPhoneState.cleanup(); + mPhoneState=null; } - if(mContext != null) - mContext = null; + if (mPhonebook != null) { + mPhonebook.cleanup(); + mPhonebook = null; + } + if (mNativeAvailable) { + cleanupNative(); + mNativeAvailable = false; + } + mService = null; + mAdapter = null; } private class Disconnected extends State { @@ -1025,7 +1039,7 @@ final class HeadsetStateMachine extends StateMachine { !isInCall()) { try { - mContext.startActivity(sVoiceCommandIntent); + mService.startActivity(sVoiceCommandIntent); } catch (ActivityNotFoundException e) { atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0); return; @@ -1136,12 +1150,9 @@ final class HeadsetStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); - AdapterService svc = AdapterService.getAdapterService(); - if (svc != null) { - svc.onProfileConnectionStateChanged(device, BluetoothProfile.HEADSET, newState, prevState); - } + mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET, newState, prevState); } private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { @@ -1149,7 +1160,7 @@ final class HeadsetStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - mContext.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); + mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM); if (DBG) log("Audio state " + device + ": " + prevState + "->" + newState); } @@ -1265,7 +1276,7 @@ final class HeadsetStateMachine extends StateMachine { Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mService.startActivity(intent); // TODO(BT) continue send OK reults code after call starts // hold wait lock, start a timer, set wait call flag // Get call started indication from bluetooth phone @@ -1487,7 +1498,7 @@ final class HeadsetStateMachine extends StateMachine { Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts(SCHEME_TEL, dialNumber, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mService.startActivity(intent); } } diff --git a/src/com/android/bluetooth/hid/HidService.java b/src/com/android/bluetooth/hid/HidService.java index 1f76a2ccb..c522c66cc 100755 --- a/src/com/android/bluetooth/hid/HidService.java +++ b/src/com/android/bluetooth/hid/HidService.java @@ -4,8 +4,6 @@ package com.android.bluetooth.hid; -import android.app.Service; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothProfile; @@ -27,7 +25,6 @@ import java.util.List; import java.util.Map; import com.android.bluetooth.Utils; import android.content.pm.PackageManager; -import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; /** @@ -38,7 +35,9 @@ import com.android.bluetooth.btservice.ProfileService; public class HidService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "HidService"; + private Map<BluetoothDevice, Integer> mInputDevices; + private boolean mNativeAvailable; private static final int MESSAGE_CONNECT = 1; private static final int MESSAGE_DISCONNECT = 2; @@ -61,38 +60,35 @@ public class HidService extends ProfileService { return TAG; } - @Override - public IBinder onBind(Intent intent) { - log("onBind"); - return mBinder; + public IProfileServiceBinder initBinder() { + return new BluetoothInputDeviceBinder(this); } - protected boolean start() { - if(mInputDevices != null) { - mInputDevices.clear(); - mInputDevices = null; - } mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>()); initializeNative(); - + mNativeAvailable=true; return true; } protected boolean stop() { if (DBG) log("Stopping Bluetooth HidService"); - cleanupNative(); + return true; + } + + protected boolean cleanup() { + if (mNativeAvailable) { + cleanupNative(); + mNativeAvailable=false; + } if(mInputDevices != null) { mInputDevices.clear(); mInputDevices = null; } - return true; } - - private final Handler mHandler = new Handler() { @Override @@ -101,7 +97,7 @@ public class HidService extends ProfileService { case MESSAGE_CONNECT: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if (!connectHidNative(getByteAddress(device)) ) { + if (!connectHidNative(Utils.getByteAddress(device)) ) { broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING); broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); break; @@ -111,7 +107,7 @@ public class HidService extends ProfileService { case MESSAGE_DISCONNECT: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if (!disconnectHidNative(getByteAddress(device)) ) { + if (!disconnectHidNative(Utils.getByteAddress(device)) ) { broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING); broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED); break; @@ -128,7 +124,7 @@ public class HidService extends ProfileService { case MESSAGE_GET_PROTOCOL_MODE: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if(!getProtocolModeNative(getByteAddress(device)) ) { + if(!getProtocolModeNative(Utils.getByteAddress(device)) ) { Log.e(TAG, "Error: get protocol mode native returns false"); } } @@ -144,7 +140,7 @@ public class HidService extends ProfileService { case MESSAGE_VIRTUAL_UNPLUG: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if(!virtualUnPlugNative(getByteAddress(device))) { + if(!virtualUnPlugNative(Utils.getByteAddress(device))) { Log.e(TAG, "Error: virtual unplug native returns false"); } } @@ -154,7 +150,7 @@ public class HidService extends ProfileService { BluetoothDevice device = (BluetoothDevice) msg.obj; byte protocolMode = (byte) msg.arg1; log("sending set protocol mode(" + protocolMode + ")"); - if(!setProtocolModeNative(getByteAddress(device), protocolMode)) { + if(!setProtocolModeNative(Utils.getByteAddress(device), protocolMode)) { Log.e(TAG, "Error: set protocol mode native returns false"); } } @@ -166,7 +162,7 @@ public class HidService extends ProfileService { byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE); byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID); int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE); - if(!getReportNative(getByteAddress(device), reportType, reportId, bufferSize)) { + if(!getReportNative(Utils.getByteAddress(device), reportType, reportId, bufferSize)) { Log.e(TAG, "Error: get report native returns false"); } } @@ -177,7 +173,7 @@ public class HidService extends ProfileService { Bundle data = msg.getData(); byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE); String report = data.getString(BluetoothInputDevice.EXTRA_REPORT); - if(!setReportNative(getByteAddress(device), reportType, report)) { + if(!setReportNative(Utils.getByteAddress(device), reportType, report)) { Log.e(TAG, "Error: set report native returns false"); } } @@ -187,7 +183,7 @@ public class HidService extends ProfileService { BluetoothDevice device = (BluetoothDevice) msg.obj; Bundle data = msg.getData(); String report = data.getString(BluetoothInputDevice.EXTRA_REPORT); - if(!sendDataNative(getByteAddress(device), report)) { + if(!sendDataNative(Utils.getByteAddress(device), report)) { Log.e(TAG, "Error: send data native returns false"); } } @@ -206,179 +202,264 @@ public class HidService extends ProfileService { /** * Handlers for incoming service calls */ - private final IBluetoothInputDevice.Stub mBinder = new IBluetoothInputDevice.Stub() { - public boolean connect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) { - Log.e(TAG, "Hid Device not disconnected: " + device); - return false; - } - if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) { - Log.e(TAG, "Hid Device PRIORITY_OFF: " + device); - return false; - } + private static class BluetoothInputDeviceBinder extends IBluetoothInputDevice.Stub implements IProfileServiceBinder{ + private HidService mService; + public BluetoothInputDeviceBinder(HidService svc) { + mService = svc; + } - Message msg = mHandler.obtainMessage(MESSAGE_CONNECT); - msg.obj = device; - mHandler.sendMessage(msg); + public boolean cleanup() { + mService = null; return true; } + private HidService getService() { + if (mService != null && mService.isAvailable()) { + return mService; + } + return null; + } + + public boolean connect(BluetoothDevice device) { + HidService service = getService(); + if (service == null) return false; + return service.connect(device); + } + public boolean disconnect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT); - msg.obj = device; - mHandler.sendMessage(msg); - return true; + HidService service = getService(); + if (service == null) return false; + return service.disconnect(device); } public int getConnectionState(BluetoothDevice device) { - if (mInputDevices.get(device) == null) { - return BluetoothInputDevice.STATE_DISCONNECTED; - } - return mInputDevices.get(device); + HidService service = getService(); + if (service == null) return BluetoothInputDevice.STATE_DISCONNECTED; + return service.getConnectionState(device); } public List<BluetoothDevice> getConnectedDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> devices = getDevicesMatchingConnectionStates( + return getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED}); - return devices; } public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); - - for (BluetoothDevice device: mInputDevices.keySet()) { - int inputDeviceState = getConnectionState(device); - for (int state : states) { - if (state == inputDeviceState) { - inputDevices.add(device); - break; - } - } - } - return inputDevices; + HidService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getDevicesMatchingConnectionStates(states); } public boolean setPriority(BluetoothDevice device, int priority) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - Settings.Secure.putInt(getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - priority); - if (DBG) log("Saved priority " + device + " = " + priority); - return true; + HidService service = getService(); + if (service == null) return false; + return service.setPriority(device, priority); } public int getPriority(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int priority = Settings.Secure.getInt(getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - BluetoothProfile.PRIORITY_UNDEFINED); - return priority; + HidService service = getService(); + if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; + return service.getPriority(device); } + /* The following APIs regarding test app for compliance */ public boolean getProtocolMode(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE); - msg.obj = device; - mHandler.sendMessage(msg); - return true; - /* String objectPath = getObjectPathFromAddress(device.getAddress()); - return getProtocolModeInputDeviceNative(objectPath);*/ + HidService service = getService(); + if (service == null) return false; + return service.getProtocolMode(device); } public boolean virtualUnplug(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG); - msg.obj = device; - mHandler.sendMessage(msg); - - return true; + HidService service = getService(); + if (service == null) return false; + return service.virtualUnplug(device); } public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE); - msg.obj = device; - msg.arg1 = protocolMode; - mHandler.sendMessage(msg); - return true ; + HidService service = getService(); + if (service == null) return false; + return service.setProtocolMode(device, protocolMode); } - + public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT); - msg.obj = device; - Bundle data = new Bundle(); - data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType); - data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId); - data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize); - msg.setData(data); - mHandler.sendMessage(msg); - return true ; + HidService service = getService(); + if (service == null) return false; + return service.getReport(device, reportType, reportId, bufferSize) ; } public boolean setReport(BluetoothDevice device, byte reportType, String report) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT); - msg.obj = device; - Bundle data = new Bundle(); - data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType); - data.putString(BluetoothInputDevice.EXTRA_REPORT, report); - msg.setData(data); - mHandler.sendMessage(msg); - return true ; - + HidService service = getService(); + if (service == null) return false; + return service.setReport(device, reportType, report); } public boolean sendData(BluetoothDevice device, String report) { - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - int state = this.getConnectionState(device); - if (state != BluetoothInputDevice.STATE_CONNECTED) { - return false; + HidService service = getService(); + if (service == null) return false; + return service.sendData(device, report); + } + }; + + //APIs + boolean connect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) { + Log.e(TAG, "Hid Device not disconnected: " + device); + return false; + } + if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) { + Log.e(TAG, "Hid Device PRIORITY_OFF: " + device); + return false; + } + + Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device); + mHandler.sendMessage(msg); + return true; + } + + boolean disconnect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device); + mHandler.sendMessage(msg); + return true; + } + + int getConnectionState(BluetoothDevice device) { + if (mInputDevices.get(device) == null) { + return BluetoothInputDevice.STATE_DISCONNECTED; + } + return mInputDevices.get(device); + } + + List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mInputDevices.keySet()) { + int inputDeviceState = getConnectionState(device); + for (int state : states) { + if (state == inputDeviceState) { + inputDevices.add(device); + break; + } } + } + return inputDevices; + } - return sendDataNative(getByteAddress(device), report); - /*Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA); - msg.obj = device; - Bundle data = new Bundle(); - data.putString(BluetoothInputDevice.EXTRA_REPORT, report); - msg.setData(data); - mHandler.sendMessage(msg); - return true ;*/ + boolean setPriority(BluetoothDevice device, int priority) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + Settings.Secure.putInt(getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + priority); + if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority); + return true; + } + + int getPriority(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int priority = Settings.Secure.getInt(getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + BluetoothProfile.PRIORITY_UNDEFINED); + return priority; + } + + /* The following APIs regarding test app for compliance */ + boolean getProtocolMode(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; } - }; + Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE,device); + mHandler.sendMessage(msg); + return true; + /* String objectPath = getObjectPathFromAddress(device.getAddress()); + return getProtocolModeInputDeviceNative(objectPath);*/ + } + boolean virtualUnplug(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG,device); + mHandler.sendMessage(msg); + return true; + } + + boolean setProtocolMode(BluetoothDevice device, int protocolMode) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE); + msg.obj = device; + msg.arg1 = protocolMode; + mHandler.sendMessage(msg); + return true ; + } + + boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT); + msg.obj = device; + Bundle data = new Bundle(); + data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType); + data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId); + data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize); + msg.setData(data); + mHandler.sendMessage(msg); + return true ; + } + + boolean setReport(BluetoothDevice device, byte reportType, String report) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT); + msg.obj = device; + Bundle data = new Bundle(); + data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType); + data.putString(BluetoothInputDevice.EXTRA_REPORT, report); + msg.setData(data); + mHandler.sendMessage(msg); + return true ; + + } + + boolean sendData(BluetoothDevice device, String report) { + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + int state = this.getConnectionState(device); + if (state != BluetoothInputDevice.STATE_CONNECTED) { + return false; + } + + return sendDataNative(Utils.getByteAddress(device), report); + /*Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA); + msg.obj = device; + Bundle data = new Bundle(); + data.putString(BluetoothInputDevice.EXTRA_REPORT, report); + msg.setData(data); + mHandler.sendMessage(msg); + return true ;*/ + } + private void onGetProtocolMode(byte[] address, int mode) { Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE); msg.obj = address; @@ -418,10 +499,7 @@ public class HidService extends ProfileService { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); sendBroadcast(intent, BLUETOOTH_PERM); if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); - AdapterService svc = AdapterService.getAdapterService(); - if (svc != null) { - svc.onProfileConnectionStateChanged(device, BluetoothProfile.INPUT_DEVICE, newState, prevState); - } + notifyProfileConnectionStateChanged(device, BluetoothProfile.INPUT_DEVICE, newState, prevState); } private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) { @@ -441,15 +519,7 @@ public class HidService extends ProfileService { sendBroadcast(intent, BLUETOOTH_PERM); } - private BluetoothDevice getDevice(byte[] address) { - return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); - } - - private byte[] getByteAddress(BluetoothDevice device) { - return Utils.getBytesFromAddress(device.getAddress()); - } - - private int convertHalState(int halState) { + private static int convertHalState(int halState) { switch (halState) { case CONN_STATE_CONNECTED: return BluetoothProfile.STATE_CONNECTED; @@ -465,7 +535,6 @@ public class HidService extends ProfileService { } } - // Constants matching Hal header file bt_hh.h // bthh_connection_state_t private final static int CONN_STATE_CONNECTED = 0; diff --git a/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java index ec0f8b26e..0bdc6df60 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java +++ b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java @@ -91,6 +91,7 @@ public class BluetoothOppIncomingFileConfirmActivity extends AlertActivity imple @Override protected void onCreate(Bundle savedInstanceState) { + if (V) Log.d(TAG, "onCreate(): action = " + getIntent().getAction()); super.onCreate(savedInstanceState); Intent intent = getIntent(); diff --git a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java index 8f2b9107f..f9e96f558 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java +++ b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java @@ -151,7 +151,7 @@ public class BluetoothOppLauncherActivity extends Activity { Constants.THIS_PACKAGE_NAME); in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS, BluetoothOppReceiver.class.getName()); - + if (V) {Log.d(TAG,"Launching " +BluetoothDevicePicker.ACTION_LAUNCH );} this.startActivity(in1); } } else if (action.equals(Constants.ACTION_OPEN)) { diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java index ea7e4b20a..f234203ce 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java +++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java @@ -59,7 +59,7 @@ import java.lang.Thread; */ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { - private static final String TAG = "BtOpp ObexClient"; + private static final String TAG = "BtOppObexClient"; private static final boolean D = Constants.DEBUG; private static final boolean V = Constants.VERBOSE; diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java index 1d21e7c80..97fe37b82 100755 --- a/src/com/android/bluetooth/pan/PanService.java +++ b/src/com/android/bluetooth/pan/PanService.java @@ -5,7 +5,6 @@ package com.android.bluetooth.pan; import android.app.Service; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; @@ -36,7 +35,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import com.android.bluetooth.Utils; -import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; /** @@ -56,6 +54,7 @@ public class PanService extends ProfileService { private ArrayList<String> mBluetoothIfaceAddresses; private int mMaxPanDevices; private String mPanIfName; + private boolean mNativeAvailable; private static final int MESSAGE_CONNECT = 1; private static final int MESSAGE_DISCONNECT = 2; @@ -71,30 +70,11 @@ public class PanService extends ProfileService { return TAG; } - @Override - public IBinder onBind(Intent intent) { - log("onBind"); - return mBinder; + public IProfileServiceBinder initBinder() { + return new BluetoothPanBinder(this); } protected boolean start() { - - //Cleanup referenced objects here - - if(mPanDevices != null) { - mPanDevices.clear(); - mPanDevices = null; - } - if(mBluetoothIfaceAddresses != null) { - mBluetoothIfaceAddresses.clear(); - mBluetoothIfaceAddresses = null; - } - if(mPanIfName != null) - mPanIfName = null; - if(mHandler != null) - mHandler.removeCallbacksAndMessages(null); - - if (DBG) log("start"); mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); mBluetoothIfaceAddresses = new ArrayList<String>(); try { @@ -103,17 +83,22 @@ public class PanService extends ProfileService { } catch (NotFoundException e) { mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; } - initializeNative(); + mNativeAvailable=true; return true; } protected boolean stop() { if (DBG) log("stop"); + mHandler.removeCallbacksAndMessages(null); + return true; + } - //Cleanup native - cleanupNative(); - + protected boolean cleanup() { + if (mNativeAvailable) { + cleanupNative(); + mNativeAvailable=false; + } if(mPanDevices != null) { mPanDevices.clear(); mPanDevices = null; @@ -124,20 +109,17 @@ public class PanService extends ProfileService { } if(mPanIfName != null) mPanIfName = null; - if(mHandler != null) - mHandler.removeCallbacksAndMessages(null); return true; } private final Handler mHandler = new Handler() { - @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_CONNECT: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if (!connectPanNative(getByteAddress(device), BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) { + if (!connectPanNative(Utils.getByteAddress(device), BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) { handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING, BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_DISCONNECTED, @@ -149,7 +131,7 @@ public class PanService extends ProfileService { case MESSAGE_DISCONNECT: { BluetoothDevice device = (BluetoothDevice) msg.obj; - if (!disconnectPanNative(getByteAddress(device)) ) { + if (!disconnectPanNative(Utils.getByteAddress(device)) ) { handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); handlePanDeviceStateChange(device, mPanIfName, BluetoothProfile.STATE_DISCONNECTED, @@ -175,82 +157,146 @@ public class PanService extends ProfileService { /** * Handlers for incoming service calls */ - private final IBluetoothPan.Stub mBinder = new IBluetoothPan.Stub() { - public boolean connect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { - Log.e(TAG, "Pan Device not disconnected: " + device); - return false; - } - Message msg = mHandler.obtainMessage(MESSAGE_CONNECT); - msg.obj = device; - mHandler.sendMessage(msg); + private static class BluetoothPanBinder extends IBluetoothPan.Stub implements IProfileServiceBinder { + private PanService mService; + public BluetoothPanBinder(PanService svc) { + mService = svc; + } + public boolean cleanup() { + mService = null; return true; } - + private PanService getService() { + if (mService != null && mService.isAvailable()) { + return mService; + } + return null; + } + public boolean connect(BluetoothDevice device) { + PanService service = getService(); + if (service == null) return false; + return service.connect(device); + } public boolean disconnect(BluetoothDevice device) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT); - msg.obj = device; - mHandler.sendMessage(msg); - return true; + PanService service = getService(); + if (service == null) return false; + return service.disconnect(device); } - public int getConnectionState(BluetoothDevice device) { - BluetoothPanDevice panDevice = mPanDevices.get(device); - if (panDevice == null) { - return BluetoothPan.STATE_DISCONNECTED; - } - return panDevice.mState; + PanService service = getService(); + if (service == null) return BluetoothPan.STATE_DISCONNECTED; + return service.getConnectionState(device); } private boolean isPanNapOn() { - if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); - return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0; + PanService service = getService(); + if (service == null) return false; + return service.isPanNapOn(); } private boolean isPanUOn() { if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); - return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0; + PanService service = getService(); + return service.isPanUOn(); } public boolean isTetheringOn() { // TODO(BT) have a variable marking the on/off state - return mTetherOn; + PanService service = getService(); + if (service == null) return false; + return service.isTetheringOn(); } - public void setBluetoothTethering(boolean value) { - if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn); - enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); - if(mTetherOn != value) { - //drop any existing panu or pan-nap connection when changing the tethering state - mTetherOn = value; - List<BluetoothDevice> DevList = getConnectedDevices(); - for(BluetoothDevice dev : DevList) - disconnect(dev); - } + PanService service = getService(); + if (service == null) return; + if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn); + service.setBluetoothTethering(value); } public List<BluetoothDevice> getConnectedDevices() { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> devices = getDevicesMatchingConnectionStates( - new int[] {BluetoothProfile.STATE_CONNECTED}); - return devices; + PanService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getConnectedDevices(); } public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>(); - - for (BluetoothDevice device: mPanDevices.keySet()) { - int panDeviceState = getConnectionState(device); - for (int state : states) { - if (state == panDeviceState) { - panDevices.add(device); - break; - } + PanService service = getService(); + if (service == null) return new ArrayList<BluetoothDevice>(0); + return service.getDevicesMatchingConnectionStates(states); + } + }; + + boolean connect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { + Log.e(TAG, "Pan Device not disconnected: " + device); + return false; + } + Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device); + mHandler.sendMessage(msg); + return true; + } + + boolean disconnect(BluetoothDevice device) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device); + mHandler.sendMessage(msg); + return true; + } + + int getConnectionState(BluetoothDevice device) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + if (panDevice == null) { + return BluetoothPan.STATE_DISCONNECTED; + } + return panDevice.mState; + } + + boolean isPanNapOn() { + if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); + return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0; + } + boolean isPanUOn() { + if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); + return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0; + } + boolean isTetheringOn() { + // TODO(BT) have a variable marking the on/off state + return mTetherOn; + } + + void setBluetoothTethering(boolean value) { + if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn); + enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); + if(mTetherOn != value) { + //drop any existing panu or pan-nap connection when changing the tethering state + mTetherOn = value; + List<BluetoothDevice> DevList = getConnectedDevices(); + for(BluetoothDevice dev : DevList) + disconnect(dev); + } + } + + List<BluetoothDevice> getConnectedDevices() { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + List<BluetoothDevice> devices = getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED}); + return devices; + } + + List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mPanDevices.keySet()) { + int panDeviceState = getConnectionState(device); + for (int state : states) { + if (state == panDeviceState) { + panDevices.add(device); + break; } } - return panDevices; } - }; + return panDevices; + } + static protected class ConnectState { public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) { this.addr = address; @@ -277,15 +323,8 @@ public class PanService extends ProfileService { if(error == 0) mPanIfName = ifname; } - private BluetoothDevice getDevice(byte[] address) { - return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); - } - private byte[] getByteAddress(BluetoothDevice device) { - return Utils.getBytesFromAddress(device.getAddress()); - } - - private int convertHalState(int halState) { + private static int convertHalState(int halState) { switch (halState) { case CONN_STATE_CONNECTED: return BluetoothProfile.STATE_CONNECTED; @@ -321,7 +360,7 @@ public class PanService extends ProfileService { if (state == BluetoothProfile.STATE_CONNECTED) { if(!mTetherOn) { Log.d(TAG, "handlePanDeviceStateChange bluetooth tethering is off, drop the connection"); - disconnectPanNative(getByteAddress(device)); + disconnectPanNative(Utils.getByteAddress(device)); return; } ifaceAddr = enableTethering(iface); @@ -382,10 +421,7 @@ public class PanService extends ProfileService { if (DBG) Log.d(TAG, "Pan Device state : device: " + device + " State:" + prevState + "->" + state); - AdapterService svc = AdapterService.getAdapterService(); - if (svc != null) { - svc.onProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState); - } + notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState); } // configured when we start tethering @@ -512,4 +548,5 @@ public class PanService extends ProfileService { private native boolean disconnectPanNative(byte[] btAddress); private native boolean enablePanNative(int local_role); private native int getPanLocalRoleNative(); + } diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java index 5d7be8462..982a32271 100755 --- a/src/com/android/bluetooth/pbap/BluetoothPbapService.java +++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java @@ -641,16 +641,11 @@ public class BluetoothPbapService extends Service { intent.putExtra(BluetoothPbap.PBAP_STATE, mState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); sendBroadcast(intent, BLUETOOTH_PERM); - //if (mBluetoothService != null) { AdapterService s = AdapterService.getAdapterService(); if (s != null) { s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP, mState, prevState); } - /* - } else { - Log.e(TAG, "null mBluetoothService"); - }*/ } } |