diff options
author | Ugo Yu <ugoyu@google.com> | 2019-03-26 21:38:08 +0800 |
---|---|---|
committer | Ugo Yu <ugoyu@google.com> | 2019-05-02 17:25:52 +0800 |
commit | 1652b94a121a19863c554551c57406cbef504ce8 (patch) | |
tree | c482242e179383379c6e7be3c6e15998115d81fd /framework/java/android/bluetooth/BluetoothHeadsetClient.java | |
parent | cb43dd0c105f49903ba1108f4d20f4b0d1ab99f1 (diff) |
Fix binder leakage when turning off Bluetooth
* In current design, Bluetooth AdapterState stops all BR/EDR
profiles' service and triggers onServiceDisconnected callback to
all binder clients before BluetoothManagerService invokes
onBluetoothStateChange(false), which means unbind service
would never be called in framework.
* Do unbind service when onServiceDisconnected is invoked.
* Move profile binder logic to BluetoothProfileConnector except:
- BluetoothHeadset: its binder logic is in BluetoothManagerService
- BluetoothPbap: it has an individual ServiceListener
Bug: 129037442
Bug: 129437895
Test: Bluetooth ON/OFF stress test.
adb shell dumpsys activity services | egrep "com.android.bluetooth"
to check whether AppBindRecord for com.android.bluetooth grows
Merged-In: Id0d85866d386962b94d2d966f0a864b1da165d13
Change-Id: Id0d85866d386962b94d2d966f0a864b1da165d13
Diffstat (limited to 'framework/java/android/bluetooth/BluetoothHeadsetClient.java')
-rw-r--r-- | framework/java/android/bluetooth/BluetoothHeadsetClient.java | 195 |
1 files changed, 64 insertions, 131 deletions
diff --git a/framework/java/android/bluetooth/BluetoothHeadsetClient.java b/framework/java/android/bluetooth/BluetoothHeadsetClient.java index ec18d42698..05833b5f57 100644 --- a/framework/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/framework/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,10 +17,7 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -366,73 +363,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { + private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, + "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - Intent intent = new Intent( - IBluetoothHeadsetClient.class.getName()); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + public IBluetoothHeadsetClient getServiceInterface(IBinder service) { + return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - mContext.getUser())) { - Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /** @@ -443,27 +389,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothHeadsetClient getService() { + return mProfileConnector.getService(); } /** @@ -480,7 +410,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -503,7 +434,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -524,7 +456,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -547,7 +480,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -569,7 +503,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -589,7 +524,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -611,7 +547,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -637,7 +574,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.startVoiceRecognition(device); @@ -662,7 +600,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.stopVoiceRecognition(device); @@ -682,7 +621,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentCalls(device); @@ -702,7 +642,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentAgEvents(device); @@ -726,7 +667,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.acceptCall(device, flag); @@ -747,7 +689,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.holdCall(device); @@ -773,7 +716,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.rejectCall(device); @@ -803,7 +747,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.terminateCall(device, call); @@ -831,7 +776,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.enterPrivateMode(device, index); @@ -858,7 +804,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.explicitCallTransfer(device); @@ -881,7 +828,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.dial(device, number); @@ -905,7 +853,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendDTMF(device, code); @@ -931,7 +880,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getLastVoiceTagNumber(device); @@ -951,7 +901,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioState(device); @@ -974,7 +925,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { service.setAudioRouteAllowed(device, allowed); @@ -996,7 +948,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioRouteAllowed(device); @@ -1020,7 +973,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.connectAudio(device); @@ -1044,7 +998,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.disconnectAudio(device); @@ -1065,7 +1020,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getCurrentAgFeatures(device); @@ -1079,29 +1035,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return null; } - - private final ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, - BluetoothHeadsetClient.this); - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } |