Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 979f2e79 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun
Browse files

Re-register callbacks when service is connected

When a callback is registered to
BluetoothHapClient, BluetoothLeAudio and BluetoothLeBroadcast,
and their corresponding service is not available,
they register callbacks when the service is available.

Before this change, they did when BT is up, which could be
before the service is available and it failed.

This change fixes the timing by listening to the service connection.
It also removes unnecessary broadcast receivers.

Tag: #feature
Bug: 267461828
Test: atest BluetoothInstrumentationTests
Change-Id: I12f49241b49fc2f793e7c2a25c66629aacd4c53f
parent 5a00b850
Loading
Loading
Loading
Loading
+38 −47
Original line number Diff line number Diff line
@@ -65,6 +65,43 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable

    private CloseGuard mCloseGuard;

    private final class HapClientServiceListener extends ForwardingServiceListener {
        HapClientServiceListener(ServiceListener listener) {
            super(listener);
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            try {
                if (profile == HAP_CLIENT) {
                    // re-register the service-to-app callback
                    synchronized (mCallbackExecutorMap) {
                        if (mCallbackExecutorMap.isEmpty()) {
                            return;
                        }

                        try {
                            final IBluetoothHapClient service = getService();
                            if (service != null) {
                                final SynchronousResultReceiver<Integer> recv =
                                        SynchronousResultReceiver.get();
                                service.registerCallback(mCallback, mAttributionSource, recv);
                                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
                            }
                        } catch (TimeoutException e) {
                            Log.e(TAG, e.toString() + "\n"
                                    + Log.getStackTraceString(new Throwable()));
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                }
            } finally {
                super.onServiceConnected(profile, proxy);
            }
        }
    }

    /**
     * This class provides callbacks mechanism for the BluetoothHapClient profile.
     *
@@ -470,34 +507,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
                }
            };

    @SuppressLint("AndroidFrameworkBluetoothPermission")
    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
            new IBluetoothStateChangeCallback.Stub() {
                public void onBluetoothStateChange(boolean up) {
                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (up) {
                        // re-register the service-to-app callback
                        synchronized (mCallbackExecutorMap) {
                            if (mCallbackExecutorMap.isEmpty()) return;

                            try {
                                final IBluetoothHapClient service = getService();
                                if (service != null) {
                                    final SynchronousResultReceiver<Integer> recv =
                                            SynchronousResultReceiver.get();
                                    service.registerCallback(mCallback, mAttributionSource, recv);
                                    recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
                                }
                            } catch (TimeoutException e) {
                                Log.e(TAG, e.toString() + "\n"
                                        + Log.getStackTraceString(new Throwable()));
                            } catch (RemoteException e) {
                                throw e.rethrowFromSystemServer();
                            }
                        }
                    }
                }
            };

    /**
     * Create a BluetoothHapClient proxy object for interacting with the local
@@ -506,16 +515,7 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
    /*package*/ BluetoothHapClient(Context context, ServiceListener listener) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mAttributionSource = mAdapter.getAttributionSource();
        mProfileConnector.connect(context, listener);

        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        mProfileConnector.connect(context, new HapClientServiceListener(listener));

        mCloseGuard = new CloseGuard();
        mCloseGuard.open("close");
@@ -536,15 +536,6 @@ public final class BluetoothHapClient implements BluetoothProfile, AutoCloseable
    public void close() {
        if (VDBG) log("close()");

        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "", e);
            }
        }

        mProfileConnector.disconnect();
    }

+36 −50
Original line number Diff line number Diff line
@@ -69,6 +69,41 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {

    private CloseGuard mCloseGuard;

    private final class LeAudioServiceListener extends ForwardingServiceListener {
        LeAudioServiceListener(ServiceListener listener) {
            super(listener);
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            try {
                if (profile == LE_AUDIO) {
                    // re-register the service-to-app callback
                    synchronized (mCallbackExecutorMap) {
                        if (mCallbackExecutorMap.isEmpty()) {
                            return;
                        }
                        try {
                            final IBluetoothLeAudio service = getService();
                            if (service != null) {
                                final SynchronousResultReceiver<Integer> recv =
                                        SynchronousResultReceiver.get();
                                service.registerCallback(mCallback, mAttributionSource, recv);
                                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
                            }
                        } catch (TimeoutException e) {
                            Log.e(TAG, "Failed to register callback", e);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                }
            } finally {
                super.onServiceConnected(profile, proxy);
            }
        }
    }

    /**
     * This class provides a callback that is invoked when audio codec config changes on
     * the remote device.
@@ -640,37 +675,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
                }
    };


    @SuppressLint("AndroidFrameworkBluetoothPermission")
    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
            new IBluetoothStateChangeCallback.Stub() {
                public void onBluetoothStateChange(boolean up) {
                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (up) {
                        // re-register the service-to-app callback
                        synchronized (mCallbackExecutorMap) {
                            if (!mCallbackExecutorMap.isEmpty()) {
                                try {
                                    final IBluetoothLeAudio service = getService();
                                    if (service != null) {
                                        final SynchronousResultReceiver<Integer> recv =
                                                SynchronousResultReceiver.get();
                                        service.registerCallback(mCallback,
                                                mAttributionSource, recv);
                                        recv.awaitResultNoInterrupt(getSyncTimeout())
                                                .getValue(null);
                                    }
                                } catch (TimeoutException e) {
                                    Log.e(TAG, "Failed to register callback", e);
                                } catch (RemoteException e) {
                                    throw e.rethrowFromSystemServer();
                                }
                            }
                        }
                    }
                }
            };

    /**
     * Create a BluetoothLeAudio proxy object for interacting with the local
     * Bluetooth LeAudio service.
@@ -679,16 +683,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
            BluetoothAdapter adapter) {
        mAdapter = adapter;
        mAttributionSource = adapter.getAttributionSource();
        mProfileConnector.connect(context, listener);

        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        mProfileConnector.connect(context, new LeAudioServiceListener(listener));

        mCloseGuard = new CloseGuard();
        mCloseGuard.open("close");
@@ -697,15 +692,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
    /** @hide */
    @Override
    public void close() {
        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "", e);
            }
        }

        mProfileConnector.disconnect();
    }

+34 −45
Original line number Diff line number Diff line
@@ -170,15 +170,20 @@ public final class BluetoothLeBroadcast implements AutoCloseable, BluetoothProfi
        }
    };

    @SuppressLint("AndroidFrameworkBluetoothPermission")
    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
            new IBluetoothStateChangeCallback.Stub() {
                public void onBluetoothStateChange(boolean up) {
                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
                    if (up) {
    private final class BluetoothLeBroadcastServiceListener extends ForwardingServiceListener {
        BluetoothLeBroadcastServiceListener(ServiceListener listener) {
            super(listener);
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            try {
                if (profile == LE_AUDIO_BROADCAST) {
                    // re-register the service-to-app callback
                    synchronized (mCallbackExecutorMap) {
                            if (!mCallbackExecutorMap.isEmpty()) {
                        if (mCallbackExecutorMap.isEmpty()) {
                            return;
                        }
                        try {
                            final IBluetoothLeAudio service = getService();
                            if (service != null) {
@@ -197,9 +202,11 @@ public final class BluetoothLeBroadcast implements AutoCloseable, BluetoothProfi
                        }
                    }
                }
            } finally {
                super.onServiceConnected(profile, proxy);
            }
        }
    }
            };

    /**
     * Interface for receiving events related to Broadcast Source
@@ -329,16 +336,7 @@ public final class BluetoothLeBroadcast implements AutoCloseable, BluetoothProfi
    /*package*/ BluetoothLeBroadcast(Context context, BluetoothProfile.ServiceListener listener) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mAttributionSource = mAdapter.getAttributionSource();
        mProfileConnector.connect(context, listener);

        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        mProfileConnector.connect(context, new BluetoothLeBroadcastServiceListener(listener));

        mCloseGuard = new CloseGuard();
        mCloseGuard.open("close");
@@ -878,15 +876,6 @@ public final class BluetoothLeBroadcast implements AutoCloseable, BluetoothProfi
    public void close() {
        if (VDBG) log("close()");

        IBluetoothManager mgr = mAdapter.getBluetoothManager();
        if (mgr != null) {
            try {
                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "", e);
            }
        }

        mProfileConnector.disconnect();
    }

+28 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.bluetooth;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -415,6 +416,33 @@ public interface BluetoothProfile {
        void onServiceDisconnected(int profile);
    }

    /**
     * A service listener that forwards methods calls to the given listener.
     * This can be used to override specific method.
     * @hide
     */
    class ForwardingServiceListener implements ServiceListener {
        private final ServiceListener mListener;

        ForwardingServiceListener(@Nullable ServiceListener listener) {
            mListener = listener;
        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (mListener != null) {
                mListener.onServiceConnected(profile, proxy);
            }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            if (mListener != null) {
                mListener.onServiceDisconnected(profile);
            }
        }
    }

    /**
     * Convert an integer value of connection state into human readable string
     *