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

Commit 16ab0ced authored by William Escande's avatar William Escande
Browse files

Hap: Use wrapper to manage broadcast callback

Bug: 311772251
Flag: Exempt refactor
Test: atest HapClientServiceTest
Change-Id: Ib954ba1e02d81013dfa335eb2f30e6ba5c11b389
parent 0209fb30
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.os.Build;
import android.os.ParcelUuid;
import android.os.PowerExemptionManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -92,6 +93,7 @@ import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public final class Utils {
    private static final String TAG = "BluetoothUtils";
@@ -1258,6 +1260,23 @@ public final class Utils {
                || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
    }

    /** A {@link Consumer} that automatically ignores any {@link RemoteException}s. */
    @FunctionalInterface
    @SuppressWarnings("FunctionalInterfaceMethodChanged")
    public interface RemoteExceptionIgnoringConsumer<T> extends Consumer<T> {
        /** Called by {@code accept}. */
        void acceptOrThrow(T t) throws RemoteException;

        @Override
        default void accept(T t) {
            try {
                acceptOrThrow(t);
            } catch (RemoteException ex) {
                // Ignore RemoteException
            }
        }
    }

    /**
     * Returns the longest prefix of a string for which the UTF-8 encoding fits into the given
     * number of bytes, with the additional guarantee that the string is not truncated in the middle
+55 −209
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.sysprop.BluetoothProperties;
import android.util.Log;

@@ -565,6 +564,17 @@ public class HapClientService extends ProfileService {
        return null;
    }

    private void broadcastToClient(
            Utils.RemoteExceptionIgnoringConsumer<IBluetoothHapClientCallback> consumer) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                consumer.accept(mCallbacks.getBroadcastItem(i));
            }
            mCallbacks.finishBroadcast();
        }
    }

    /**
     * Selects the currently active preset for a HA device
     *
@@ -573,21 +583,8 @@ public class HapClientService extends ProfileService {
     */
    public void selectPreset(BluetoothDevice device, int presetIndex) {
        if (presetIndex == BluetoothHapClient.PRESET_INDEX_UNAVAILABLE) {
            synchronized (mCallbacks) {
                int n = mCallbacks.beginBroadcast();
                for (int i = 0; i < n; i++) {
                    try {
                        mCallbacks
                                .getBroadcastItem(i)
                                .onPresetSelectionFailed(
                                        device,
                                        BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX);
                    } catch (RemoteException e) {
                        continue;
                    }
                }
                mCallbacks.finishBroadcast();
            }
            int status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
            broadcastToClient(cb -> cb.onPresetSelectionFailed(device, status));
            return;
        }

@@ -601,28 +598,14 @@ public class HapClientService extends ProfileService {
     * @param presetIndex is an index of one of the available presets
     */
    public void selectPresetForGroup(int groupId, int presetIndex) {
        int status = BluetoothStatusCodes.SUCCESS;

        if (!isGroupIdValid(groupId)) {
            status = BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
        } else if (!isPresetIndexValid(groupId, presetIndex)) {
            status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
        }

        if (status != BluetoothStatusCodes.SUCCESS) {
            synchronized (mCallbacks) {
                int n = mCallbacks.beginBroadcast();
                for (int i = 0; i < n; i++) {
                    try {
                        mCallbacks
                                .getBroadcastItem(i)
                                .onPresetSelectionForGroupFailed(groupId, status);
                    } catch (RemoteException e) {
                        continue;
                    }
                }
                mCallbacks.finishBroadcast();
            int status = BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
            broadcastToClient(cb -> cb.onPresetSelectionForGroupFailed(groupId, status));
            return;
        }
        if (!isPresetIndexValid(groupId, presetIndex)) {
            int status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
            broadcastToClient(cb -> cb.onPresetSelectionForGroupFailed(groupId, status));
            return;
        }

@@ -741,57 +724,12 @@ public class HapClientService extends ProfileService {
        List current_presets = mPresetsMap.get(device);
        if (current_presets == null) return;

        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onPresetInfoChanged(
        broadcastToClient(
                cb ->
                        cb.onPresetInfoChanged(
                                device,
                                current_presets,
                                    stackEventPresetInfoReasonToProfileStatus(infoReason));
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifyPresetInfoForGroupChanged(int groupId, int infoReason) {
        List<BluetoothDevice> all_group_devices = getGroupDevices(groupId);
        for (BluetoothDevice dev : all_group_devices) {
            notifyPresetInfoChanged(dev, infoReason);
        }
    }

    private void notifyFeaturesAvailable(BluetoothDevice device, int features) {
        Log.d(TAG, "HAP device: " + device + ", features: " + String.format("0x%04X", features));
    }

    private void notifyActivePresetChanged(
            BluetoothDevice device, int presetIndex, int reasonCode) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onPresetSelected(device, presetIndex, reasonCode);
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifyActivePresetChangedForGroup(int groupId, int presetIndex, int reasonCode) {
        List<BluetoothDevice> all_group_devices = getGroupDevices(groupId);
        for (BluetoothDevice dev : all_group_devices) {
            notifyActivePresetChanged(dev, presetIndex, reasonCode);
        }
                                stackEventPresetInfoReasonToProfileStatus(infoReason)));
    }

    private int stackEventStatusToProfileStatus(int statusCode) {
@@ -815,74 +753,6 @@ public class HapClientService extends ProfileService {
        }
    }

    private void notifySelectActivePresetFailed(BluetoothDevice device, int statusCode) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onPresetSelectionFailed(
                                    device, stackEventStatusToProfileStatus(statusCode));
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifySelectActivePresetForGroupFailed(int groupId, int statusCode) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onPresetSelectionForGroupFailed(
                                    groupId, stackEventStatusToProfileStatus(statusCode));
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifySetPresetNameFailed(BluetoothDevice device, int statusCode) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onSetPresetNameFailed(
                                    device, stackEventStatusToProfileStatus(statusCode));
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private void notifySetPresetNameForGroupFailed(int groupId, int statusCode) {
        synchronized (mCallbacks) {
            int n = mCallbacks.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mCallbacks
                            .getBroadcastItem(i)
                            .onSetPresetNameForGroupFailed(
                                    groupId, stackEventStatusToProfileStatus(statusCode));
                } catch (RemoteException e) {
                    continue;
                }
            }
            mCallbacks.finishBroadcast();
        }
    }

    private boolean isPresetIndexValid(BluetoothDevice device, int presetIndex) {
        if (presetIndex == BluetoothHapClient.PRESET_INDEX_UNAVAILABLE) return false;

@@ -927,21 +797,8 @@ public class HapClientService extends ProfileService {
     */
    public void setPresetName(BluetoothDevice device, int presetIndex, String name) {
        if (!isPresetIndexValid(device, presetIndex)) {
            synchronized (mCallbacks) {
                int n = mCallbacks.beginBroadcast();
                for (int i = 0; i < n; i++) {
                    try {
                        mCallbacks
                                .getBroadcastItem(i)
                                .onSetPresetNameFailed(
                                        device,
                                        BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX);
                    } catch (RemoteException e) {
                        continue;
                    }
                }
                mCallbacks.finishBroadcast();
            }
            int status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
            broadcastToClient(cb -> cb.onSetPresetNameFailed(device, status));
            return;
        }
        // WARNING: We should check cache if preset exists and is writable, but then we would still
@@ -958,27 +815,14 @@ public class HapClientService extends ProfileService {
     * @param name is a new name for a preset
     */
    public void setPresetNameForGroup(int groupId, int presetIndex, String name) {
        int status = BluetoothStatusCodes.SUCCESS;

        if (!isGroupIdValid(groupId)) {
            status = BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
        } else if (!isPresetIndexValid(groupId, presetIndex)) {
            status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
        }
        if (status != BluetoothStatusCodes.SUCCESS) {
            synchronized (mCallbacks) {
                int n = mCallbacks.beginBroadcast();
                for (int i = 0; i < n; i++) {
                    try {
                        mCallbacks
                                .getBroadcastItem(i)
                                .onSetPresetNameForGroupFailed(groupId, status);
                    } catch (RemoteException e) {
                        continue;
                    }
                }
                mCallbacks.finishBroadcast();
            int status = BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID;
            broadcastToClient(cb -> cb.onSetPresetNameForGroupFailed(groupId, status));
            return;
        }
        if (!isPresetIndexValid(groupId, presetIndex)) {
            int status = BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
            broadcastToClient(cb -> cb.onSetPresetNameForGroupFailed(groupId, status));
            return;
        }

@@ -1097,30 +941,31 @@ public class HapClientService extends ProfileService {

                    if (device != null) {
                        mDeviceFeaturesMap.put(device, features);
                        notifyFeaturesAvailable(device, features);
                        Log.d(
                                TAG,
                                ("device=" + device)
                                        + (" features=" + String.format("0x%04X", features)));
                    }
                }
                return;

            case (HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED):
                {
                    int currentPresetIndex = stackEvent.valueInt1;
                    int presetIndex = stackEvent.valueInt1;
                    int groupId = stackEvent.valueInt2;
                    // FIXME: Add app request queueing to support other reasons
                    int reason = BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST;

                    if (device != null) {
                        mDeviceCurrentPresetMap.put(device, currentPresetIndex);
                        // FIXME: Add app request queueing to support other reasons
                        int reasonCode = BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST;
                        notifyActivePresetChanged(device, currentPresetIndex, reasonCode);
                        mDeviceCurrentPresetMap.put(device, presetIndex);
                        broadcastToClient(cb -> cb.onPresetSelected(device, presetIndex, reason));

                    } else if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                        List<BluetoothDevice> all_group_devices = getGroupDevices(groupId);
                        for (BluetoothDevice dev : all_group_devices) {
                            mDeviceCurrentPresetMap.put(dev, currentPresetIndex);
                            mDeviceCurrentPresetMap.put(dev, presetIndex);
                            broadcastToClient(cb -> cb.onPresetSelected(dev, presetIndex, reason));
                        }
                        // FIXME: Add app request queueing to support other reasons
                        int reasonCode = BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST;
                        notifyActivePresetChangedForGroup(groupId, currentPresetIndex, reasonCode);
                    }
                }
                return;
@@ -1128,12 +973,13 @@ public class HapClientService extends ProfileService {
            case (HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECT_ERROR):
                {
                    int groupId = stackEvent.valueInt2;
                    int statusCode = stackEvent.valueInt1;
                    int status = stackEventStatusToProfileStatus(stackEvent.valueInt1);

                    if (device != null) {
                        notifySelectActivePresetFailed(device, statusCode);
                        broadcastToClient(cb -> cb.onPresetSelectionFailed(device, status));
                    } else if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                        notifySelectActivePresetForGroupFailed(groupId, statusCode);
                        broadcastToClient(
                                cb -> cb.onPresetSelectionForGroupFailed(groupId, status));
                    }
                }
                break;
@@ -1152,21 +998,21 @@ public class HapClientService extends ProfileService {
                        List<BluetoothDevice> all_group_devices = getGroupDevices(groupId);
                        for (BluetoothDevice dev : all_group_devices) {
                            updateDevicePresetsCache(dev, infoReason, presets);
                            notifyPresetInfoChanged(dev, infoReason);
                        }
                        notifyPresetInfoForGroupChanged(groupId, infoReason);
                    }
                }
                return;

            case (HapClientStackEvent.EVENT_TYPE_ON_PRESET_NAME_SET_ERROR):
                {
                    int statusCode = stackEvent.valueInt1;
                    int status = stackEventStatusToProfileStatus(stackEvent.valueInt1);
                    int groupId = stackEvent.valueInt3;

                    if (device != null) {
                        notifySetPresetNameFailed(device, statusCode);
                        broadcastToClient(cb -> cb.onSetPresetNameFailed(device, status));
                    } else if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                        notifySetPresetNameForGroupFailed(groupId, statusCode);
                        broadcastToClient(cb -> cb.onSetPresetNameForGroupFailed(groupId, status));
                    }
                }
                break;
@@ -1181,7 +1027,6 @@ public class HapClientService extends ProfileService {
            default:
                return;
        }

    }

    private void resendToStateMachine(HapClientStackEvent stackEvent) {
@@ -1218,6 +1063,7 @@ public class HapClientService extends ProfileService {
        BluetoothHapClientBinder(HapClientService svc) {
            mService = svc;
        }

        @Override
        public void cleanup() {
            mService = null;