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

Commit 8742dc82 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "AudioDeviceBroker: strengthen setcommunicationDevice implementation"

parents f7de5d91 71a41063
Loading
Loading
Loading
Loading
+105 −102
Original line number Diff line number Diff line
@@ -286,22 +286,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
        }

        synchronized (mSetModeLock) {
            synchronized (mDeviceStateLock) {
                AudioDeviceAttributes device = null;
                if (on) {
                    device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
                } else {
                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
                    if (client == null || !client.requestsSpeakerphone()) {
                        return;
                    }
                }
                postSetCommunicationRouteForClient(new CommunicationClientInfo(
                        cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource));
            }
        }
        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
                cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
                on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
    }

    /**
@@ -311,6 +298,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
     * @param device Device selected or null to unselect.
     * @param eventSource for logging purposes
     */

    private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;

    /*package*/ boolean setCommunicationDevice(
            IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {

@@ -318,21 +308,53 @@ import java.util.concurrent.atomic.AtomicBoolean;
            Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid);
        }

        synchronized (mSetModeLock) {
            synchronized (mDeviceStateLock) {
                AudioDeviceAttributes deviceAttr = null;
                if (device != null) {
                    deviceAttr = new AudioDeviceAttributes(device);
                } else {
                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
                    if (client == null) {
                        return false;
        AudioDeviceAttributes deviceAttr =
                (device != null) ? new AudioDeviceAttributes(device) : null;
        CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, pid, deviceAttr,
                device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true);
        postSetCommunicationDeviceForClient(deviceInfo);
        boolean status;
        synchronized (deviceInfo) {
            final long start = System.currentTimeMillis();
            long elapsed = 0;
            while (deviceInfo.mWaitForStatus) {
                try {
                    deviceInfo.wait(SET_COMMUNICATION_DEVICE_TIMEOUT_MS - elapsed);
                } catch (InterruptedException e) {
                    elapsed = System.currentTimeMillis() - start;
                    if (elapsed >= SET_COMMUNICATION_DEVICE_TIMEOUT_MS) {
                        deviceInfo.mStatus = false;
                        deviceInfo.mWaitForStatus = false;
                    }
                }
            }
                postSetCommunicationRouteForClient(new CommunicationClientInfo(
                        cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource));
            status = deviceInfo.mStatus;
        }
        return status;
    }

    /**
     * Sets or resets the communication device for matching client. If no client matches and the
     * request is to reset for a given device (deviceInfo.mOn == false), the method is a noop.
     * @param deviceInfo information on the device and requester {@link #CommunicationDeviceInfo}
     * @return true if the communication device is set or reset
     */
    @GuardedBy("mDeviceStateLock")
    /*package*/ boolean onSetCommunicationDeviceForClient(CommunicationDeviceInfo deviceInfo) {
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
        }
        if (!deviceInfo.mOn) {
            CommunicationRouteClient client = getCommunicationRouteClientForPid(deviceInfo.mPid);
            if (client == null || (deviceInfo.mDevice != null
                    && !deviceInfo.mDevice.equals(client.getDevice()))) {
                return false;
            }
        }

        AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
        setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mPid, device,
                deviceInfo.mScoAudioMode, deviceInfo.mEventSource);
        return true;
    }

@@ -390,7 +412,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
            mBtHelper.stopBluetoothSco(eventSource);
        }

        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
        updateCommunicationRoute(eventSource);
    }

    /**
@@ -424,7 +446,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
        CommunicationRouteClient crc = topCommunicationRouteClient();
        AudioDeviceAttributes device = crc != null ? crc.getDevice() : null;
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "requestedCommunicationDevice, device: "
            Log.v(TAG, "requestedCommunicationDevice: "
                    + device + " mAudioModeOwner: " + mAudioModeOwner.toString());
        }
        return device;
@@ -822,37 +844,22 @@ import java.util.concurrent.atomic.AtomicBoolean;
                @NonNull String eventSource) {

        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
        }

        synchronized (mSetModeLock) {
            synchronized (mDeviceStateLock) {
                AudioDeviceAttributes device =
                        new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");

                postSetCommunicationRouteForClient(new CommunicationClientInfo(
                        cb, pid, device, scoAudioMode, eventSource));
            }
            Log.v(TAG, "startBluetoothScoForClient, pid: " + pid);
        }
        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
                cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
                true, scoAudioMode, eventSource, false));
    }

    /*package*/ void stopBluetoothScoForClient(
                        IBinder cb, int pid, @NonNull String eventSource) {

        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
        }

        synchronized (mSetModeLock) {
            synchronized (mDeviceStateLock) {
                CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
                if (client == null || !client.requestsBluetoothSco()) {
                    return;
                }
                postSetCommunicationRouteForClient(new CommunicationClientInfo(
                        cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource));
            }
            Log.v(TAG, "stopBluetoothScoForClient, pid: " + pid);
        }
        postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
                cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
                false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
    }

    /*package*/ int setPreferredDevicesForStrategySync(int strategy,
@@ -990,7 +997,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
    }

    //---------------------------------------------------------------------
    // Message handling on behalf of helper classes
    // Message handling on behalf of helper classes.
    // Each of these methods posts a message to mBrokerHandler message queue.
    /*package*/ void postBroadcastScoConnectionState(int state) {
        sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
    }
@@ -1046,28 +1054,34 @@ import java.util.concurrent.atomic.AtomicBoolean;
        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource);
    }

    /*package*/ void postSetCommunicationRouteForClient(CommunicationClientInfo info) {
        sendLMsgNoDelay(MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT, SENDMSG_QUEUE, info);
    /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
        sendLMsgNoDelay(MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT, SENDMSG_QUEUE, info);
    }

    /*package*/ void postScoAudioStateChanged(int state) {
        sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state);
    }

    /*package*/ static final class CommunicationClientInfo {
        final @NonNull IBinder mCb;
        final int mPid;
        final @NonNull AudioDeviceAttributes mDevice;
        final int mScoAudioMode;
        final @NonNull String mEventSource;
    /*package*/ static final class CommunicationDeviceInfo {
        final @NonNull IBinder mCb; // Identifies the requesting client for death handler
        final int mPid; // Requester process ID
        final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset.
        final boolean mOn; // true if setting, false if resetting
        final int mScoAudioMode; // only used for SCO: requested audio mode
        final @NonNull String mEventSource; // caller identifier for logging
        boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent)
        boolean mStatus = false; // completion status only used if mWaitForStatus is true

        CommunicationClientInfo(@NonNull IBinder cb, int pid, @NonNull AudioDeviceAttributes device,
                int scoAudioMode, @NonNull String eventSource) {
        CommunicationDeviceInfo(@NonNull IBinder cb, int pid,
                @Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode,
                @NonNull String eventSource, boolean waitForStatus) {
            mCb = cb;
            mPid = pid;
            mDevice = device;
            mOn = on;
            mScoAudioMode = scoAudioMode;
            mEventSource = eventSource;
            mWaitForStatus = waitForStatus;
        }

        // redefine equality op so we can match messages intended for this client
@@ -1079,21 +1093,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
            if (this == o) {
                return true;
            }
            if (!(o instanceof CommunicationClientInfo)) {
            if (!(o instanceof CommunicationDeviceInfo)) {
                return false;
            }

            return mCb.equals(((CommunicationClientInfo) o).mCb)
                    && mPid == ((CommunicationClientInfo) o).mPid;
            return mCb.equals(((CommunicationDeviceInfo) o).mCb)
                    && mPid == ((CommunicationDeviceInfo) o).mPid;
        }

        @Override
        public String toString() {
            return "CommunicationClientInfo mCb=" + mCb.toString()
            return "CommunicationDeviceInfo mCb=" + mCb.toString()
                    + " mPid=" + mPid
                    +"mDevice=" + mDevice.toString()
                    + " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
                    + " mOn=" + mOn
                    + " mScoAudioMode=" + mScoAudioMode
                    +"mEventSource=" + mEventSource;
                    + " mEventSource=" + mEventSource
                    + " mWaitForStatus=" + mWaitForStatus
                    + " mStatus=" + mStatus;
        }
    }

@@ -1297,7 +1314,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
                            updateActiveCommunicationDevice();
                            mDeviceInventory.onRestoreDevices();
                            mBtHelper.onAudioServerDiedRestoreA2dp();
                            onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
                            updateCommunicationRoute("MSG_RESTORE_DEVICES");
                        }
                    }
                    break;
@@ -1392,28 +1409,27 @@ import java.util.concurrent.atomic.AtomicBoolean;
                    }
                    break;

                case MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT:
                case MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT:
                    CommunicationDeviceInfo deviceInfo = (CommunicationDeviceInfo) msg.obj;
                    boolean status;
                    synchronized (mSetModeLock) {
                        synchronized (mDeviceStateLock) {
                            CommunicationClientInfo info = (CommunicationClientInfo) msg.obj;
                            setCommunicationRouteForClient(info.mCb, info.mPid, info.mDevice,
                                    info.mScoAudioMode, info.mEventSource);
                            status = onSetCommunicationDeviceForClient(deviceInfo);
                        }
                    }
                    break;

                case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
                    synchronized (mSetModeLock) {
                        synchronized (mDeviceStateLock) {
                            onUpdateCommunicationRouteClient((String) msg.obj);
                    synchronized (deviceInfo) {
                        if (deviceInfo.mWaitForStatus) {
                            deviceInfo.mStatus = status;
                            deviceInfo.mWaitForStatus = false;
                            deviceInfo.notify();
                        }
                    }
                    break;

                case MSG_L_UPDATE_COMMUNICATION_ROUTE:
                case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
                    synchronized (mSetModeLock) {
                        synchronized (mDeviceStateLock) {
                            onUpdateCommunicationRoute((String) msg.obj);
                            onUpdateCommunicationRouteClient((String) msg.obj);
                        }
                    }
                    break;
@@ -1568,8 +1584,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;

    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
    private static final int MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT = 42;
    private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
    private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;

@@ -1793,18 +1808,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
        AudioDeviceAttributes getDevice() {
            return mDevice;
        }

        boolean requestsBluetoothSco() {
            return mDevice != null
                    && mDevice.getType()
                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
        }

        boolean requestsSpeakerphone() {
            return mDevice != null
                    && mDevice.getType()
                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
        }
    }

    // @GuardedBy("mSetModeLock")
@@ -1852,14 +1855,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
     */
    // @GuardedBy("mSetModeLock")
    @GuardedBy("mDeviceStateLock")
    private void onUpdateCommunicationRoute(String eventSource) {
    private void updateCommunicationRoute(String eventSource) {
        AudioDeviceAttributes preferredCommunicationDevice = preferredCommunicationDevice();
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "onUpdateCommunicationRoute, preferredCommunicationDevice: "
            Log.v(TAG, "updateCommunicationRoute, preferredCommunicationDevice: "
                    + preferredCommunicationDevice + " eventSource: " + eventSource);
        }
        AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
                "onUpdateCommunicationRoute, preferredCommunicationDevice: "
                "updateCommunicationRoute, preferredCommunicationDevice: "
                + preferredCommunicationDevice + " eventSource: " + eventSource)));

        if (preferredCommunicationDevice == null
@@ -1895,7 +1898,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
    // @GuardedBy("mSetModeLock")
    @GuardedBy("mDeviceStateLock")
    private void onUpdateCommunicationRouteClient(String eventSource) {
        onUpdateCommunicationRoute(eventSource);
        updateCommunicationRoute(eventSource);
        CommunicationRouteClient crc = topCommunicationRouteClient();
        if (AudioService.DEBUG_COMM_RTE) {
            Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "