Loading android/app/src/com/android/bluetooth/tbs/BluetoothGattServerProxy.java +22 −0 Original line number Diff line number Diff line Loading @@ -79,16 +79,38 @@ public class BluetoothGattServerProxy { return mBluetoothGattServer.getService(uuid); } /** * See {@link android.bluetooth.BluetoothGattServer#sendResponse( * android.bluetooth.BluetoothDevice, int, int, int, byte[])} */ public boolean sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) { return mBluetoothGattServer.sendResponse(device, requestId, status, offset, value); } /** * See {@link android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged( * android.bluetooth.BluetoothDevice, BluetoothGattCharacteristic, boolean, byte[])}. */ public int notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm, byte[] value) { return mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, confirm, value); } /** * See {@link android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged( * android.bluetooth.BluetoothDevice, BluetoothGattCharacteristic, boolean)}. */ public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { return mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, confirm); } /** * Get connected devices * @return list of connected devices */ public List<BluetoothDevice> getConnectedDevices() { return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER); } Loading android/app/src/com/android/bluetooth/tbs/TbsGatt.java +103 −24 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -141,6 +142,8 @@ public class TbsGatt { private final GattCharacteristic mTerminationReasonCharacteristic; private final GattCharacteristic mIncomingCallCharacteristic; private final GattCharacteristic mCallFriendlyNameCharacteristic; private boolean mSilentMode = false; private Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>(); private List<BluetoothDevice> mSubscribers = new ArrayList<>(); private BluetoothGattServerProxy mBluetoothGattServer; private Handler mHandler; Loading Loading @@ -354,6 +357,14 @@ public class TbsGatt { return mSubscribers.contains(device); } private void notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false, value); } } private void notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (mBluetoothGattServer != null) { Loading @@ -361,6 +372,13 @@ public class TbsGatt { } } public void notifyWithValue(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (isSubscribed(device)) { notifyCharacteristicChanged(device, characteristic, value); } } public void notify(BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (isSubscribed(device)) { notifyCharacteristicChanged(device, characteristic); Loading Loading @@ -435,6 +453,11 @@ public class TbsGatt { return super.setValue(value); } public boolean notifyWithValue(BluetoothDevice device, byte[] value) { mNotifier.notifyWithValue(device, this, value); return true; } public boolean clearValue(boolean notify) { boolean success = super.setValue(new byte[0]); if (success && notify && isNotifiable()) { Loading Loading @@ -604,34 +627,85 @@ public class TbsGatt { return mBearerListCurrentCallsCharacteristic.setValue(stream.toByteArray()); } private boolean updateStatusFlags(int flag, boolean set) { Integer valueInt = mStatusFlagsCharacteristic .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0); private boolean updateStatusFlags(BluetoothDevice device, int valueInt) { /* uint16_t */ byte[] value = new byte[2]; value[0] = (byte) (valueInt & 0xFF); value[1] = (byte) ((valueInt >> 8) & 0xFF); return mStatusFlagsCharacteristic.notifyWithValue(device, value); } if (((valueInt & flag) != 0) == set) { private boolean updateStatusFlagsInbandRingtone(BluetoothDevice device, boolean set) { boolean entryExist = mStatusFlagValue.containsKey(device); if (entryExist && (((mStatusFlagValue.get(device) & STATUS_FLAG_INBAND_RINGTONE_ENABLED) != 0) == set)) { Log.i(TAG, "Silent mode already set for " + device); return false; } valueInt ^= flag; Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0; valueInt ^= STATUS_FLAG_INBAND_RINGTONE_ENABLED; return mStatusFlagsCharacteristic.setValue(valueInt, BluetoothGattCharacteristic.FORMAT_UINT16, 0); if (entryExist) { mStatusFlagValue.replace(device, valueInt); } else { mStatusFlagValue.put(device, valueInt); } return updateStatusFlags(device, valueInt); } public boolean setInbandRingtoneFlag() { return updateStatusFlags(STATUS_FLAG_INBAND_RINGTONE_ENABLED, true); private boolean updateStatusFlagsSilentMode(boolean set) { mSilentMode = set; for (BluetoothDevice device: mSubscribers) { boolean entryExist = mStatusFlagValue.containsKey(device); if (entryExist && (((mStatusFlagValue.get(device) & STATUS_FLAG_SILENT_MODE_ENABLED) != 0) == set)) { Log.i(TAG, "Silent mode already set for " + device); continue; } Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0; valueInt ^= STATUS_FLAG_SILENT_MODE_ENABLED; if (entryExist) { mStatusFlagValue.replace(device, valueInt); } else { mStatusFlagValue.put(device, valueInt); } updateStatusFlags(device, valueInt); } return true; } public boolean clearInbandRingtoneFlag() { return updateStatusFlags(STATUS_FLAG_INBAND_RINGTONE_ENABLED, false); /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set * @return true, when notification has been sent, false otherwise */ public boolean setInbandRingtoneFlag(BluetoothDevice device) { return updateStatusFlagsInbandRingtone(device, true); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been cleared * @return true, when notification has been sent, false otherwise */ public boolean clearInbandRingtoneFlag(BluetoothDevice device) { return updateStatusFlagsInbandRingtone(device, false); } public boolean setSilentModeFlag() { return updateStatusFlags(STATUS_FLAG_SILENT_MODE_ENABLED, true); return updateStatusFlagsSilentMode(true); } public boolean clearSilentModeFlag() { return updateStatusFlags(STATUS_FLAG_SILENT_MODE_ENABLED, false); return updateStatusFlagsSilentMode(false); } private void setCallControlPointOptionalOpcodes(boolean isLocalHoldOpcodeSupported, Loading Loading @@ -810,18 +884,23 @@ public class TbsGatt { if (DBG) { Log.d(TAG, "onCharacteristicReadRequest: device=" + device); } byte[] value; if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) { value = new byte[2]; int valueInt = mSilentMode ? STATUS_FLAG_SILENT_MODE_ENABLED : 0; if (mStatusFlagValue.containsKey(device)) { valueInt = mStatusFlagValue.get(device); } else if (mCallback.isInbandRingtoneEnabled(device)) { valueInt |= STATUS_FLAG_INBAND_RINGTONE_ENABLED; } value[0] = (byte) (valueInt & 0xFF); value[1] = (byte) ((valueInt >> 8) & 0xFF); } else { GattCharacteristic gattCharacteristic = (GattCharacteristic) characteristic; byte[] value = gattCharacteristic.getValue(); value = gattCharacteristic.getValue(); if (value == null) { value = new byte[0]; } /* TODO: Properly handle caching for multiple devices. * This patch assumes, LeAudio services just uses single value * for inband ringtone */ if (characteristic.getUuid().equals(UUID_STATUS_FLAGS) && (value.length == 2)) { if (mCallback.isInbandRingtoneEnabled(device)) { value[0] = (byte) (value[0] | STATUS_FLAG_INBAND_RINGTONE_ENABLED); } } int status; Loading android/app/src/com/android/bluetooth/tbs/TbsGeneric.java +28 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,34 @@ public class TbsGeneric { mIsInitialized = false; } /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set */ public synchronized void setInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGatt == null) { Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); return; } mTbsGatt.setInbandRingtoneFlag(device); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been cleared */ public synchronized void clearInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGatt == null) { Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); return; } mTbsGatt.clearInbandRingtoneFlag(device); } private synchronized boolean isSilentModeEnabled() { return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL; } Loading android/app/src/com/android/bluetooth/tbs/TbsService.java +30 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.bluetooth.tbs; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeCall; import android.bluetooth.IBluetoothLeCallControl; import android.bluetooth.IBluetoothLeCallControlCallback; Loading Loading @@ -132,6 +133,35 @@ public class TbsService extends ProfileService { sTbsService = instance; } /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set */ public void setInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGeneric == null) { Log.i(TAG, "setInbandRingtoneSupport, mTbsGeneric not available"); return; } mTbsGeneric.setInbandRingtoneSupport(device); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been clear */ public void clearInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGeneric == null) { Log.i(TAG, "clearInbandRingtoneSupport, mTbsGeneric not available"); return; } mTbsGeneric.clearInbandRingtoneSupport(device); } /** Binder object: must be a static class or memory leak may occur */ @VisibleForTesting static class TbsServerBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder { Loading android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java +184 −56 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
android/app/src/com/android/bluetooth/tbs/BluetoothGattServerProxy.java +22 −0 Original line number Diff line number Diff line Loading @@ -79,16 +79,38 @@ public class BluetoothGattServerProxy { return mBluetoothGattServer.getService(uuid); } /** * See {@link android.bluetooth.BluetoothGattServer#sendResponse( * android.bluetooth.BluetoothDevice, int, int, int, byte[])} */ public boolean sendResponse(BluetoothDevice device, int requestId, int status, int offset, byte[] value) { return mBluetoothGattServer.sendResponse(device, requestId, status, offset, value); } /** * See {@link android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged( * android.bluetooth.BluetoothDevice, BluetoothGattCharacteristic, boolean, byte[])}. */ public int notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm, byte[] value) { return mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, confirm, value); } /** * See {@link android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged( * android.bluetooth.BluetoothDevice, BluetoothGattCharacteristic, boolean)}. */ public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { return mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, confirm); } /** * Get connected devices * @return list of connected devices */ public List<BluetoothDevice> getConnectedDevices() { return mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER); } Loading
android/app/src/com/android/bluetooth/tbs/TbsGatt.java +103 −24 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -141,6 +142,8 @@ public class TbsGatt { private final GattCharacteristic mTerminationReasonCharacteristic; private final GattCharacteristic mIncomingCallCharacteristic; private final GattCharacteristic mCallFriendlyNameCharacteristic; private boolean mSilentMode = false; private Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>(); private List<BluetoothDevice> mSubscribers = new ArrayList<>(); private BluetoothGattServerProxy mBluetoothGattServer; private Handler mHandler; Loading Loading @@ -354,6 +357,14 @@ public class TbsGatt { return mSubscribers.contains(device); } private void notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false, value); } } private void notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (mBluetoothGattServer != null) { Loading @@ -361,6 +372,13 @@ public class TbsGatt { } } public void notifyWithValue(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (isSubscribed(device)) { notifyCharacteristicChanged(device, characteristic, value); } } public void notify(BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (isSubscribed(device)) { notifyCharacteristicChanged(device, characteristic); Loading Loading @@ -435,6 +453,11 @@ public class TbsGatt { return super.setValue(value); } public boolean notifyWithValue(BluetoothDevice device, byte[] value) { mNotifier.notifyWithValue(device, this, value); return true; } public boolean clearValue(boolean notify) { boolean success = super.setValue(new byte[0]); if (success && notify && isNotifiable()) { Loading Loading @@ -604,34 +627,85 @@ public class TbsGatt { return mBearerListCurrentCallsCharacteristic.setValue(stream.toByteArray()); } private boolean updateStatusFlags(int flag, boolean set) { Integer valueInt = mStatusFlagsCharacteristic .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0); private boolean updateStatusFlags(BluetoothDevice device, int valueInt) { /* uint16_t */ byte[] value = new byte[2]; value[0] = (byte) (valueInt & 0xFF); value[1] = (byte) ((valueInt >> 8) & 0xFF); return mStatusFlagsCharacteristic.notifyWithValue(device, value); } if (((valueInt & flag) != 0) == set) { private boolean updateStatusFlagsInbandRingtone(BluetoothDevice device, boolean set) { boolean entryExist = mStatusFlagValue.containsKey(device); if (entryExist && (((mStatusFlagValue.get(device) & STATUS_FLAG_INBAND_RINGTONE_ENABLED) != 0) == set)) { Log.i(TAG, "Silent mode already set for " + device); return false; } valueInt ^= flag; Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0; valueInt ^= STATUS_FLAG_INBAND_RINGTONE_ENABLED; return mStatusFlagsCharacteristic.setValue(valueInt, BluetoothGattCharacteristic.FORMAT_UINT16, 0); if (entryExist) { mStatusFlagValue.replace(device, valueInt); } else { mStatusFlagValue.put(device, valueInt); } return updateStatusFlags(device, valueInt); } public boolean setInbandRingtoneFlag() { return updateStatusFlags(STATUS_FLAG_INBAND_RINGTONE_ENABLED, true); private boolean updateStatusFlagsSilentMode(boolean set) { mSilentMode = set; for (BluetoothDevice device: mSubscribers) { boolean entryExist = mStatusFlagValue.containsKey(device); if (entryExist && (((mStatusFlagValue.get(device) & STATUS_FLAG_SILENT_MODE_ENABLED) != 0) == set)) { Log.i(TAG, "Silent mode already set for " + device); continue; } Integer valueInt = entryExist ? mStatusFlagValue.get(device) : 0; valueInt ^= STATUS_FLAG_SILENT_MODE_ENABLED; if (entryExist) { mStatusFlagValue.replace(device, valueInt); } else { mStatusFlagValue.put(device, valueInt); } updateStatusFlags(device, valueInt); } return true; } public boolean clearInbandRingtoneFlag() { return updateStatusFlags(STATUS_FLAG_INBAND_RINGTONE_ENABLED, false); /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set * @return true, when notification has been sent, false otherwise */ public boolean setInbandRingtoneFlag(BluetoothDevice device) { return updateStatusFlagsInbandRingtone(device, true); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been cleared * @return true, when notification has been sent, false otherwise */ public boolean clearInbandRingtoneFlag(BluetoothDevice device) { return updateStatusFlagsInbandRingtone(device, false); } public boolean setSilentModeFlag() { return updateStatusFlags(STATUS_FLAG_SILENT_MODE_ENABLED, true); return updateStatusFlagsSilentMode(true); } public boolean clearSilentModeFlag() { return updateStatusFlags(STATUS_FLAG_SILENT_MODE_ENABLED, false); return updateStatusFlagsSilentMode(false); } private void setCallControlPointOptionalOpcodes(boolean isLocalHoldOpcodeSupported, Loading Loading @@ -810,18 +884,23 @@ public class TbsGatt { if (DBG) { Log.d(TAG, "onCharacteristicReadRequest: device=" + device); } byte[] value; if (characteristic.getUuid().equals(UUID_STATUS_FLAGS)) { value = new byte[2]; int valueInt = mSilentMode ? STATUS_FLAG_SILENT_MODE_ENABLED : 0; if (mStatusFlagValue.containsKey(device)) { valueInt = mStatusFlagValue.get(device); } else if (mCallback.isInbandRingtoneEnabled(device)) { valueInt |= STATUS_FLAG_INBAND_RINGTONE_ENABLED; } value[0] = (byte) (valueInt & 0xFF); value[1] = (byte) ((valueInt >> 8) & 0xFF); } else { GattCharacteristic gattCharacteristic = (GattCharacteristic) characteristic; byte[] value = gattCharacteristic.getValue(); value = gattCharacteristic.getValue(); if (value == null) { value = new byte[0]; } /* TODO: Properly handle caching for multiple devices. * This patch assumes, LeAudio services just uses single value * for inband ringtone */ if (characteristic.getUuid().equals(UUID_STATUS_FLAGS) && (value.length == 2)) { if (mCallback.isInbandRingtoneEnabled(device)) { value[0] = (byte) (value[0] | STATUS_FLAG_INBAND_RINGTONE_ENABLED); } } int status; Loading
android/app/src/com/android/bluetooth/tbs/TbsGeneric.java +28 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,34 @@ public class TbsGeneric { mIsInitialized = false; } /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set */ public synchronized void setInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGatt == null) { Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); return; } mTbsGatt.setInbandRingtoneFlag(device); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been cleared */ public synchronized void clearInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGatt == null) { Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null"); return; } mTbsGatt.clearInbandRingtoneFlag(device); } private synchronized boolean isSilentModeEnabled() { return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL; } Loading
android/app/src/com/android/bluetooth/tbs/TbsService.java +30 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.bluetooth.tbs; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeCall; import android.bluetooth.IBluetoothLeCallControl; import android.bluetooth.IBluetoothLeCallControlCallback; Loading Loading @@ -132,6 +133,35 @@ public class TbsService extends ProfileService { sTbsService = instance; } /** * Set inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been set */ public void setInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGeneric == null) { Log.i(TAG, "setInbandRingtoneSupport, mTbsGeneric not available"); return; } mTbsGeneric.setInbandRingtoneSupport(device); } /** * Clear inband ringtone for the device. * When set, notification will be sent to given device. * * @param device device for which inband ringtone has been clear */ public void clearInbandRingtoneSupport(BluetoothDevice device) { if (mTbsGeneric == null) { Log.i(TAG, "clearInbandRingtoneSupport, mTbsGeneric not available"); return; } mTbsGeneric.clearInbandRingtoneSupport(device); } /** Binder object: must be a static class or memory leak may occur */ @VisibleForTesting static class TbsServerBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder { Loading
android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGattTest.java +184 −56 File changed.Preview size limit exceeded, changes collapsed. Show changes