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

Commit 9f3756e8 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Add support for Mute/Unmute

Bug: 231580214
Test: atest --host bluetooth_vc_test
Sponsor: @jpawlowski
Change-Id: Ib894ab01916d7eb989db6a0c28b79b321dd82ad6
(cherry picked from commit 636dc13c)
parent 1b936ca0
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -357,6 +357,60 @@ static void setVolumeGroupNative(JNIEnv* env, jobject object, jint group_id,
  sVolumeControlInterface->SetVolume(group_id, volume);
}

static void muteNative(JNIEnv* env, jobject object, jbyteArray address) {
  if (!sVolumeControlInterface) {
    LOG(ERROR) << __func__
               << ": Failed to get the Bluetooth Volume Control Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sVolumeControlInterface->Mute(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void muteGroupNative(JNIEnv* env, jobject object, jint group_id) {
  if (!sVolumeControlInterface) {
    LOG(ERROR) << __func__
               << ": Failed to get the Bluetooth Volume Control Interface";
    return;
  }
  sVolumeControlInterface->Mute(group_id);
}

static void unmuteNative(JNIEnv* env, jobject object, jbyteArray address) {
  if (!sVolumeControlInterface) {
    LOG(ERROR) << __func__
               << ": Failed to get the Bluetooth Volume Control Interface";
    return;
  }

  jbyte* addr = env->GetByteArrayElements(address, nullptr);
  if (!addr) {
    jniThrowIOException(env, EINVAL);
    return;
  }

  RawAddress* tmpraw = (RawAddress*)addr;
  sVolumeControlInterface->Unmute(*tmpraw);
  env->ReleaseByteArrayElements(address, addr, 0);
}

static void unmuteGroupNative(JNIEnv* env, jobject object, jint group_id) {
  if (!sVolumeControlInterface) {
    LOG(ERROR) << __func__
               << ": Failed to get the Bluetooth Volume Control Interface";
    return;
  }
  sVolumeControlInterface->Unmute(group_id);
}

/* Native methods for exterbak audio outputs */
static jboolean getExtAudioOutVolumeOffsetNative(JNIEnv* env, jobject object,
                                                 jbyteArray address,
@@ -494,6 +548,10 @@ static JNINativeMethod sMethods[] = {
     (void*)disconnectVolumeControlNative},
    {"setVolumeNative", "([BI)V", (void*)setVolumeNative},
    {"setVolumeGroupNative", "(II)V", (void*)setVolumeGroupNative},
    {"muteNative", "([B)V", (void*)muteNative},
    {"muteGroupNative", "(I)V", (void*)muteGroupNative},
    {"unmuteNative", "([B)V", (void*)unmuteNative},
    {"unmuteGroupNative", "(I)V", (void*)unmuteGroupNative},
    {"getExtAudioOutVolumeOffsetNative", "([BI)Z",
     (void*)getExtAudioOutVolumeOffsetNative},
    {"setExtAudioOutVolumeOffsetNative", "([BII)Z",
+42 −0
Original line number Diff line number Diff line
@@ -176,6 +176,8 @@ public class LeAudioService extends ProfileService {

    private BroadcastReceiver mBondStateChangedReceiver;
    private BroadcastReceiver mConnectionStateChangedReceiver;
    private BroadcastReceiver mMuteStateChangedReceiver;
    private int mStoredRingerMode = -1;
    private Handler mHandler = new Handler(Looper.getMainLooper());

    private final Map<Integer, Integer> mBroadcastStateMap = new HashMap<>();
@@ -239,6 +241,11 @@ public class LeAudioService extends ProfileService {
        filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
        mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
        registerReceiver(mConnectionStateChangedReceiver, filter);
        filter = new IntentFilter();
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        mMuteStateChangedReceiver = new MuteStateChangedReceiver();
        registerReceiver(mMuteStateChangedReceiver, filter);

        mLeAudioCallbacks = new RemoteCallbackList<IBluetoothLeAudioCallback>();

        int tmapRoleMask =
@@ -330,6 +337,8 @@ public class LeAudioService extends ProfileService {
        mBondStateChangedReceiver = null;
        unregisterReceiver(mConnectionStateChangedReceiver);
        mConnectionStateChangedReceiver = null;
        unregisterReceiver(mMuteStateChangedReceiver);
        mMuteStateChangedReceiver = null;

        // Destroy state machines and stop handler thread
        synchronized (mStateMachines) {
@@ -1542,6 +1551,39 @@ public class LeAudioService extends ProfileService {
        }
    }

    private synchronized boolean isSilentModeEnabled() {
        return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL;
    }

    private class MuteStateChangedReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (!AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
                return;
            }

            final String action = intent.getAction();
            if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
                int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);

                if (ringerMode < 0 || ringerMode == mStoredRingerMode) return;

                mStoredRingerMode = ringerMode;
                int activeGroupId = getActiveGroupId();
                if (activeGroupId == LE_AUDIO_GROUP_ID_INVALID) return;

                VolumeControlService service = mServiceFactory.getVolumeControlService();
                if (service == null) return;

                if (isSilentModeEnabled()) {
                    service.muteGroup(activeGroupId);
                } else {
                    service.unmuteGroup(activeGroupId);
                }
            }
        }
    }

   /**
     * Check whether can connect to a peer device.
     * The check considers a number of factors during the evaluation.
+42 −0
Original line number Diff line number Diff line
@@ -117,6 +117,44 @@ public class VolumeControlNativeInterface {
        setVolumeGroupNative(groupId, volume);
    }

     /**
     * Mute the VolumeControl volume
     * @param device
     * @param unmute
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void mute(BluetoothDevice device) {
        muteNative(getByteAddress(device));
    }

    /**
     * Mute the VolumeControl volume in the group
     * @param groupId
     * @param unmute
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void muteGroup(int groupId) {
        muteGroupNative(groupId);
    }

    /**
     * Unmute the VolumeControl volume
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void unmute(BluetoothDevice device) {
        unmuteNative(getByteAddress(device));
    }

     /**
     * Unmute the VolumeControl volume group
     * @param groupId
     * @param unmute
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public void unmuteGroup(int groupId) {
        unmuteGroupNative(groupId);
    }

    /**
     * Gets external audio output volume offset from a remote device.
     *
@@ -333,6 +371,10 @@ public class VolumeControlNativeInterface {
    private native boolean disconnectVolumeControlNative(byte[] address);
    private native void setVolumeNative(byte[] address, int volume);
    private native void setVolumeGroupNative(int groupId, int volume);
    private native void muteNative(byte[] address);
    private native void muteGroupNative(int groupId);
    private native void unmuteNative(byte[] address);
    private native void unmuteGroupNative(int groupId);
    private native boolean getExtAudioOutVolumeOffsetNative(byte[] address, int externalOutputId);
    private native boolean setExtAudioOutVolumeOffsetNative(byte[] address, int externalOutputId,
                                                                int offset);
+98 −0
Original line number Diff line number Diff line
@@ -582,6 +582,34 @@ public class VolumeControlService extends ProfileService {
        mVolumeControlNativeInterface.setVolumeGroup(groupId, volume);
    }

    /**
     * {@hide}
     */
    public void mute(BluetoothDevice device) {
        mVolumeControlNativeInterface.mute(device);
    }

    /**
     * {@hide}
     */
    public void muteGroup(int groupId) {
        mVolumeControlNativeInterface.muteGroup(groupId);
    }

    /**
     * {@hide}
     */
    public void unmute(BluetoothDevice device) {
        mVolumeControlNativeInterface.unmute(device);
    }

    /**
     * {@hide}
     */
    public void unmuteGroup(int groupId) {
        mVolumeControlNativeInterface.unmuteGroup(groupId);
    }

    void handleVolumeControlChanged(BluetoothDevice device, int groupId,
                                    int volume, boolean mute, boolean isAutonomous) {
        if (!isAutonomous) {
@@ -1113,6 +1141,76 @@ public class VolumeControlService extends ProfileService {
            }
        }

        @Override
        public void mute(BluetoothDevice device,  AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                Objects.requireNonNull(device, "device cannot be null");
                Objects.requireNonNull(source, "source cannot be null");
                Objects.requireNonNull(receiver, "receiver cannot be null");

                VolumeControlService service = getService(source);
                if (service != null) {
                    service.mute(device);
                }
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        @Override
        public void muteGroup(int groupId, AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                Objects.requireNonNull(source, "source cannot be null");
                Objects.requireNonNull(receiver, "receiver cannot be null");

                VolumeControlService service = getService(source);
                if (service != null) {
                    service.muteGroup(groupId);
                }
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        @Override
        public void unmute(BluetoothDevice device,  AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                Objects.requireNonNull(device, "device cannot be null");
                Objects.requireNonNull(source, "source cannot be null");
                Objects.requireNonNull(receiver, "receiver cannot be null");

                VolumeControlService service = getService(source);
                if (service != null) {
                    service.unmute(device);
                }
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        @Override
        public void unmuteGroup(int groupId,  AttributionSource source,
                SynchronousResultReceiver receiver) {
            try {
                Objects.requireNonNull(source, "source cannot be null");
                Objects.requireNonNull(receiver, "receiver cannot be null");

                VolumeControlService service = getService(source);
                if (service != null) {
                    service.unmuteGroup(groupId);
                }
                receiver.send(null);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        @Override
        public void registerCallback(IBluetoothVolumeControlCallback callback,
                AttributionSource source, SynchronousResultReceiver receiver) {
+10 −0
Original line number Diff line number Diff line
@@ -52,6 +52,16 @@ oneway interface IBluetoothVolumeControl {
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
    void setVolumeGroup(int group_id, int volume, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
    void mute(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
    void muteGroup(int group_id, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
    void unmute(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
    void unmuteGroup(int group_id, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    void registerCallback(in IBluetoothVolumeControlCallback callback, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
Loading