Loading android/app/src/com/android/bluetooth/tbs/TbsGatt.java +47 −76 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.bluetooth.tbs; import static android.bluetooth.BluetoothDevice.METADATA_GTBS_CCCD; import static java.util.Objects.requireNonNull; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; Loading @@ -27,7 +29,6 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattServerCallback; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.net.Uri; import android.os.Handler; import android.os.Looper; Loading @@ -48,12 +49,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; public class TbsGatt { private static final String TAG = "TbsGatt"; private static final String TAG = TbsGatt.class.getSimpleName(); private static final String UUID_PREFIX = "0000"; private static final String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb"; Loading @@ -66,51 +65,50 @@ public class TbsGatt { @VisibleForTesting static final UUID UUID_BEARER_TECHNOLOGY = makeUuid("2BB5"); @VisibleForTesting static final UUID UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST = makeUuid("2BB6"); @VisibleForTesting static final UUID UUID_BEARER_LIST_CURRENT_CALLS = makeUuid("2BB9"); @VisibleForTesting static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA"); private static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA"); @VisibleForTesting static final UUID UUID_STATUS_FLAGS = makeUuid("2BBB"); @VisibleForTesting static final UUID UUID_CALL_STATE = makeUuid("2BBD"); @VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT = makeUuid("2BBE"); @VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF"); private static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF"); @VisibleForTesting static final UUID UUID_TERMINATION_REASON = makeUuid("2BC0"); @VisibleForTesting static final UUID UUID_INCOMING_CALL = makeUuid("2BC1"); @VisibleForTesting static final UUID UUID_CALL_FRIENDLY_NAME = makeUuid("2BC2"); @VisibleForTesting static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = makeUuid("2902"); @VisibleForTesting static final int STATUS_FLAG_INBAND_RINGTONE_ENABLED = 0x0001; @VisibleForTesting static final int STATUS_FLAG_SILENT_MODE_ENABLED = 0x0002; @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001; @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05; private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001; private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00; static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00; static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01; static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02; static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03; static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04; static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01; static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00; static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01; static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02; static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03; static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04; static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02; private final Object mPendingGattOperationsLock = new Object(); private final Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>(); @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_LACK_OF_RESOURCES = 0x05; @GuardedBy("mPendingGattOperationsLock") private final Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>(); @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06; private final Map<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues = new HashMap<>(); private final Object mPendingGattOperationsLock = new Object(); private final Context mContext; private final AdapterService mAdapterService; private final TbsService mTbsService; private final Handler mHandler; private final BluetoothGattServerProxy mBluetoothGattServer; private final GattCharacteristic mBearerProviderNameCharacteristic; private final GattCharacteristic mBearerUciCharacteristic; private final GattCharacteristic mBearerTechnologyCharacteristic; Loading @@ -124,18 +122,10 @@ 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<>(); @GuardedBy("mPendingGattOperationsLock") private Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>(); private BluetoothGattServerProxy mBluetoothGattServer; private Handler mHandler; private Callback mCallback; private AdapterService mAdapterService; private HashMap<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues; private TbsService mTbsService; private boolean mSilentMode = false; private static final int LOG_NB_EVENTS = 200; private BluetoothEventLogger mEventLogger = null; Loading Loading @@ -242,12 +232,19 @@ public class TbsGatt { public byte[] mValue; } TbsGatt(TbsService tbsService) { mContext = tbsService; mAdapterService = Objects.requireNonNull( AdapterService.getAdapterService(), "AdapterService shouldn't be null when creating TbsGatt"); TbsGatt(AdapterService adapterService, TbsService tbsService) { this(adapterService, tbsService, new BluetoothGattServerProxy(adapterService)); } @VisibleForTesting TbsGatt( AdapterService adapterService, TbsService tbsService, BluetoothGattServerProxy gattServerProxy) { mTbsService = requireNonNull(tbsService); mAdapterService = requireNonNull(adapterService); mBluetoothGattServer = requireNonNull(gattServerProxy); mHandler = new Handler(Looper.getMainLooper()); mBearerProviderNameCharacteristic = new GattCharacteristic( Loading Loading @@ -317,13 +314,6 @@ public class TbsGatt { | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED); mTbsService = tbsService; mBluetoothGattServer = null; } @VisibleForTesting void setBluetoothGattServerForTesting(BluetoothGattServerProxy proxy) { mBluetoothGattServer = proxy; } public boolean init( Loading @@ -335,7 +325,6 @@ public class TbsGatt { String providerName, int technology, Callback callback) { mCccDescriptorValues = new HashMap<>(); mBearerProviderNameCharacteristic.setValue(providerName); mBearerTechnologyCharacteristic.setValue(new byte[] {(byte) (technology & 0xFF)}); mBearerUciCharacteristic.setValue(uci); Loading @@ -344,11 +333,6 @@ public class TbsGatt { setCallControlPointOptionalOpcodes(isLocalHoldOpcodeSupported, isJoinOpcodeSupported); mStatusFlagsCharacteristic.setValue(0, BluetoothGattCharacteristic.FORMAT_UINT16, 0); mCallback = callback; mHandler = new Handler(Looper.getMainLooper()); if (mBluetoothGattServer == null) { mBluetoothGattServer = new BluetoothGattServerProxy(mContext); } if (!mBluetoothGattServer.open(mGattServerCallback)) { Log.e(TAG, " Could not open Gatt server"); Loading Loading @@ -381,22 +365,14 @@ public class TbsGatt { mEventLogger.add("Initialized"); mAdapterService.registerBluetoothStateCallback( mContext.getMainExecutor(), mBluetoothStateChangeCallback); mAdapterService.getMainExecutor(), mBluetoothStateChangeCallback); return true; } public void cleanup() { mAdapterService.unregisterBluetoothStateCallback(mBluetoothStateChangeCallback); if (mBluetoothGattServer == null) { return; } mBluetoothGattServer.close(); mBluetoothGattServer = null; } public Context getContext() { return mContext; } private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) { Loading Loading @@ -506,20 +482,15 @@ public class TbsGatt { BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return; if (value == null) return; if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged( device, characteristic, false, value); } mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false, value); } private void notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return; if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false); } } public void notifyWithValue( BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { Loading android/app/src/com/android/bluetooth/tbs/TbsGeneric.java +24 −40 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.bluetooth.tbs; import static java.util.Objects.requireNonNull; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothLeCall; Loading Loading @@ -50,8 +52,7 @@ import java.util.UUID; /** Container class to store TBS instances */ public class TbsGeneric { private static final String TAG = "TbsGeneric"; private static final String TAG = TbsGeneric.class.getSimpleName(); private static final String UCI = "GTBS"; private static final String DEFAULT_PROVIDER_NAME = "none"; Loading Loading @@ -121,17 +122,20 @@ public class TbsGeneric { } } private boolean mIsInitialized = false; private TbsGatt mTbsGatt = null; private List<Bearer> mBearerList = new ArrayList<>(); private final List<Bearer> mBearerList = new ArrayList<>(); private final Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>(); private final Receiver mReceiver = new Receiver(); private final ServiceFactory mFactory = new ServiceFactory(); private final TbsGatt mTbsGatt; private final Context mContext; private boolean mIsInitialized; private int mLastIndexAssigned = TbsCall.INDEX_UNASSIGNED; private Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>(); private Bearer mForegroundBearer = null; private int mLastRequestIdAssigned = 0; private List<String> mUriSchemes = new ArrayList<>(Arrays.asList("tel")); private Receiver mReceiver = null; private int mStoredRingerMode = -1; private final ServiceFactory mFactory = new ServiceFactory(); private LeAudioService mLeAudioService; private final class Receiver extends BroadcastReceiver { Loading Loading @@ -162,9 +166,9 @@ public class TbsGeneric { } ; public synchronized boolean init(TbsGatt tbsGatt) { Log.d(TAG, "init"); mTbsGatt = tbsGatt; TbsGeneric(Context ctx, TbsGatt tbsGatt) { mTbsGatt = requireNonNull(tbsGatt); mContext = requireNonNull(ctx); int ccid = ContentControlIdKeeper.acquireCcid( Loading @@ -173,7 +177,7 @@ public class TbsGeneric { if (!isCcidValid(ccid)) { Log.e(TAG, " CCID is not valid"); cleanup(); return false; return; } if (!mTbsGatt.init( Loading @@ -187,15 +191,10 @@ public class TbsGeneric { mTbsGattCallback)) { Log.e(TAG, " TbsGatt init failed"); cleanup(); return false; return; } AudioManager audioManager = mTbsGatt.getContext().getSystemService(AudioManager.class); if (audioManager == null) { Log.w(TAG, " AudioManager is not available"); cleanup(); return false; } AudioManager audioManager = requireNonNull(mContext.getSystemService(AudioManager.class)); // read initial value of ringer mode mStoredRingerMode = audioManager.getRingerMode(); Loading @@ -206,25 +205,20 @@ public class TbsGeneric { mTbsGatt.clearSilentModeFlag(); } mReceiver = new Receiver(); IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mTbsGatt.getContext().registerReceiver(mReceiver, filter); mContext.registerReceiver(mReceiver, filter); mIsInitialized = true; return true; } public synchronized void cleanup() { Log.d(TAG, "cleanup"); if (mTbsGatt != null) { if (mReceiver != null) { mTbsGatt.getContext().unregisterReceiver(mReceiver); if (mIsInitialized) { mContext.unregisterReceiver(mReceiver); } mTbsGatt.cleanup(); mTbsGatt = null; } mIsInitialized = false; } Loading @@ -236,10 +230,8 @@ public class TbsGeneric { */ public synchronized void onDeviceAuthorizationSet(BluetoothDevice device) { // Notify TBS GATT service instance in case of pending operations if (mTbsGatt != null) { mTbsGatt.onDeviceAuthorizationSet(device); } } /** * Set inband ringtone for the device. When set, notification will be sent to given device. Loading @@ -247,10 +239,6 @@ public class TbsGeneric { * @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); } Loading @@ -260,10 +248,6 @@ public class TbsGeneric { * @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); } Loading Loading @@ -767,7 +751,7 @@ public class TbsGeneric { Log.i(TAG, "originate uri=" + uri); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mTbsGatt.getContext().startActivity(intent); mContext.startActivity(intent); mTbsGatt.setCallControlPointResult( device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, Loading android/app/src/com/android/bluetooth/tbs/TbsService.java +12 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package com.android.bluetooth.tbs; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeCall; Loading @@ -27,13 +29,13 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothLeCallControl; import android.bluetooth.IBluetoothLeCallControlCallback; import android.content.AttributionSource; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; import android.sysprop.BluetoothProperties; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; Loading @@ -44,16 +46,20 @@ import java.util.Map; import java.util.UUID; public class TbsService extends ProfileService { private static final String TAG = "TbsService"; private static final String TAG = TbsService.class.getSimpleName(); private static TbsService sTbsService; private final Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>(); private final TbsGeneric mTbsGeneric; private final TbsGeneric mTbsGeneric = new TbsGeneric(); public TbsService(AdapterService adapterService) { super(requireNonNull(adapterService)); public TbsService(Context ctx) { super(ctx); // Mark service as started setTbsService(this); mTbsGeneric = new TbsGeneric(adapterService, new TbsGatt(adapterService, this)); } public static boolean isEnabled() { Loading @@ -65,19 +71,6 @@ public class TbsService extends ProfileService { return new TbsServerBinder(this); } @Override public void start() { Log.d(TAG, "start()"); if (sTbsService != null) { throw new IllegalStateException("start() called twice"); } // Mark service as started setTbsService(this); mTbsGeneric.init(new TbsGatt(this)); } @Override public void stop() { Log.d(TAG, "stop()"); Loading Loading
android/app/src/com/android/bluetooth/tbs/TbsGatt.java +47 −76 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.bluetooth.tbs; import static android.bluetooth.BluetoothDevice.METADATA_GTBS_CCCD; import static java.util.Objects.requireNonNull; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; Loading @@ -27,7 +29,6 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattServerCallback; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.net.Uri; import android.os.Handler; import android.os.Looper; Loading @@ -48,12 +49,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; public class TbsGatt { private static final String TAG = "TbsGatt"; private static final String TAG = TbsGatt.class.getSimpleName(); private static final String UUID_PREFIX = "0000"; private static final String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb"; Loading @@ -66,51 +65,50 @@ public class TbsGatt { @VisibleForTesting static final UUID UUID_BEARER_TECHNOLOGY = makeUuid("2BB5"); @VisibleForTesting static final UUID UUID_BEARER_URI_SCHEMES_SUPPORTED_LIST = makeUuid("2BB6"); @VisibleForTesting static final UUID UUID_BEARER_LIST_CURRENT_CALLS = makeUuid("2BB9"); @VisibleForTesting static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA"); private static final UUID UUID_CONTENT_CONTROL_ID = makeUuid("2BBA"); @VisibleForTesting static final UUID UUID_STATUS_FLAGS = makeUuid("2BBB"); @VisibleForTesting static final UUID UUID_CALL_STATE = makeUuid("2BBD"); @VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT = makeUuid("2BBE"); @VisibleForTesting static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF"); private static final UUID UUID_CALL_CONTROL_POINT_OPTIONAL_OPCODES = makeUuid("2BBF"); @VisibleForTesting static final UUID UUID_TERMINATION_REASON = makeUuid("2BC0"); @VisibleForTesting static final UUID UUID_INCOMING_CALL = makeUuid("2BC1"); @VisibleForTesting static final UUID UUID_CALL_FRIENDLY_NAME = makeUuid("2BC2"); @VisibleForTesting static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = makeUuid("2902"); @VisibleForTesting static final int STATUS_FLAG_INBAND_RINGTONE_ENABLED = 0x0001; @VisibleForTesting static final int STATUS_FLAG_SILENT_MODE_ENABLED = 0x0002; @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001; @VisibleForTesting static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04; @VisibleForTesting public static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05; private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_LOCAL_HOLD = 0x0001; private static final int CALL_CONTROL_POINT_OPTIONAL_OPCODE_JOIN = 0x0002; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00; static final int CALL_CONTROL_POINT_OPCODE_ACCEPT = 0x00; static final int CALL_CONTROL_POINT_OPCODE_TERMINATE = 0x01; static final int CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD = 0x02; static final int CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE = 0x03; static final int CALL_CONTROL_POINT_OPCODE_ORIGINATE = 0x04; static final int CALL_CONTROL_POINT_OPCODE_JOIN = 0x05; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01; static final int CALL_CONTROL_POINT_RESULT_SUCCESS = 0x00; static final int CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED = 0x01; static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02; static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03; static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04; static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE = 0x02; private final Object mPendingGattOperationsLock = new Object(); private final Map<BluetoothDevice, Integer> mStatusFlagValue = new HashMap<>(); @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX = 0x03; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_STATE_MISMATCH = 0x04; @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_LACK_OF_RESOURCES = 0x05; @GuardedBy("mPendingGattOperationsLock") private final Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>(); @VisibleForTesting public static final int CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI = 0x06; private final Map<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues = new HashMap<>(); private final Object mPendingGattOperationsLock = new Object(); private final Context mContext; private final AdapterService mAdapterService; private final TbsService mTbsService; private final Handler mHandler; private final BluetoothGattServerProxy mBluetoothGattServer; private final GattCharacteristic mBearerProviderNameCharacteristic; private final GattCharacteristic mBearerUciCharacteristic; private final GattCharacteristic mBearerTechnologyCharacteristic; Loading @@ -124,18 +122,10 @@ 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<>(); @GuardedBy("mPendingGattOperationsLock") private Map<BluetoothDevice, List<GattOpContext>> mPendingGattOperations = new HashMap<>(); private BluetoothGattServerProxy mBluetoothGattServer; private Handler mHandler; private Callback mCallback; private AdapterService mAdapterService; private HashMap<BluetoothDevice, HashMap<UUID, Short>> mCccDescriptorValues; private TbsService mTbsService; private boolean mSilentMode = false; private static final int LOG_NB_EVENTS = 200; private BluetoothEventLogger mEventLogger = null; Loading Loading @@ -242,12 +232,19 @@ public class TbsGatt { public byte[] mValue; } TbsGatt(TbsService tbsService) { mContext = tbsService; mAdapterService = Objects.requireNonNull( AdapterService.getAdapterService(), "AdapterService shouldn't be null when creating TbsGatt"); TbsGatt(AdapterService adapterService, TbsService tbsService) { this(adapterService, tbsService, new BluetoothGattServerProxy(adapterService)); } @VisibleForTesting TbsGatt( AdapterService adapterService, TbsService tbsService, BluetoothGattServerProxy gattServerProxy) { mTbsService = requireNonNull(tbsService); mAdapterService = requireNonNull(adapterService); mBluetoothGattServer = requireNonNull(gattServerProxy); mHandler = new Handler(Looper.getMainLooper()); mBearerProviderNameCharacteristic = new GattCharacteristic( Loading Loading @@ -317,13 +314,6 @@ public class TbsGatt { | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED); mTbsService = tbsService; mBluetoothGattServer = null; } @VisibleForTesting void setBluetoothGattServerForTesting(BluetoothGattServerProxy proxy) { mBluetoothGattServer = proxy; } public boolean init( Loading @@ -335,7 +325,6 @@ public class TbsGatt { String providerName, int technology, Callback callback) { mCccDescriptorValues = new HashMap<>(); mBearerProviderNameCharacteristic.setValue(providerName); mBearerTechnologyCharacteristic.setValue(new byte[] {(byte) (technology & 0xFF)}); mBearerUciCharacteristic.setValue(uci); Loading @@ -344,11 +333,6 @@ public class TbsGatt { setCallControlPointOptionalOpcodes(isLocalHoldOpcodeSupported, isJoinOpcodeSupported); mStatusFlagsCharacteristic.setValue(0, BluetoothGattCharacteristic.FORMAT_UINT16, 0); mCallback = callback; mHandler = new Handler(Looper.getMainLooper()); if (mBluetoothGattServer == null) { mBluetoothGattServer = new BluetoothGattServerProxy(mContext); } if (!mBluetoothGattServer.open(mGattServerCallback)) { Log.e(TAG, " Could not open Gatt server"); Loading Loading @@ -381,22 +365,14 @@ public class TbsGatt { mEventLogger.add("Initialized"); mAdapterService.registerBluetoothStateCallback( mContext.getMainExecutor(), mBluetoothStateChangeCallback); mAdapterService.getMainExecutor(), mBluetoothStateChangeCallback); return true; } public void cleanup() { mAdapterService.unregisterBluetoothStateCallback(mBluetoothStateChangeCallback); if (mBluetoothGattServer == null) { return; } mBluetoothGattServer.close(); mBluetoothGattServer = null; } public Context getContext() { return mContext; } private void removeUuidFromMetadata(ParcelUuid charUuid, BluetoothDevice device) { Loading Loading @@ -506,20 +482,15 @@ public class TbsGatt { BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return; if (value == null) return; if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged( device, characteristic, false, value); } mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false, value); } private void notifyCharacteristicChanged( BluetoothDevice device, BluetoothGattCharacteristic characteristic) { if (getDeviceAuthorization(device) != BluetoothDevice.ACCESS_ALLOWED) return; if (mBluetoothGattServer != null) { mBluetoothGattServer.notifyCharacteristicChanged(device, characteristic, false); } } public void notifyWithValue( BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] value) { Loading
android/app/src/com/android/bluetooth/tbs/TbsGeneric.java +24 −40 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.bluetooth.tbs; import static java.util.Objects.requireNonNull; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothLeCall; Loading Loading @@ -50,8 +52,7 @@ import java.util.UUID; /** Container class to store TBS instances */ public class TbsGeneric { private static final String TAG = "TbsGeneric"; private static final String TAG = TbsGeneric.class.getSimpleName(); private static final String UCI = "GTBS"; private static final String DEFAULT_PROVIDER_NAME = "none"; Loading Loading @@ -121,17 +122,20 @@ public class TbsGeneric { } } private boolean mIsInitialized = false; private TbsGatt mTbsGatt = null; private List<Bearer> mBearerList = new ArrayList<>(); private final List<Bearer> mBearerList = new ArrayList<>(); private final Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>(); private final Receiver mReceiver = new Receiver(); private final ServiceFactory mFactory = new ServiceFactory(); private final TbsGatt mTbsGatt; private final Context mContext; private boolean mIsInitialized; private int mLastIndexAssigned = TbsCall.INDEX_UNASSIGNED; private Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>(); private Bearer mForegroundBearer = null; private int mLastRequestIdAssigned = 0; private List<String> mUriSchemes = new ArrayList<>(Arrays.asList("tel")); private Receiver mReceiver = null; private int mStoredRingerMode = -1; private final ServiceFactory mFactory = new ServiceFactory(); private LeAudioService mLeAudioService; private final class Receiver extends BroadcastReceiver { Loading Loading @@ -162,9 +166,9 @@ public class TbsGeneric { } ; public synchronized boolean init(TbsGatt tbsGatt) { Log.d(TAG, "init"); mTbsGatt = tbsGatt; TbsGeneric(Context ctx, TbsGatt tbsGatt) { mTbsGatt = requireNonNull(tbsGatt); mContext = requireNonNull(ctx); int ccid = ContentControlIdKeeper.acquireCcid( Loading @@ -173,7 +177,7 @@ public class TbsGeneric { if (!isCcidValid(ccid)) { Log.e(TAG, " CCID is not valid"); cleanup(); return false; return; } if (!mTbsGatt.init( Loading @@ -187,15 +191,10 @@ public class TbsGeneric { mTbsGattCallback)) { Log.e(TAG, " TbsGatt init failed"); cleanup(); return false; return; } AudioManager audioManager = mTbsGatt.getContext().getSystemService(AudioManager.class); if (audioManager == null) { Log.w(TAG, " AudioManager is not available"); cleanup(); return false; } AudioManager audioManager = requireNonNull(mContext.getSystemService(AudioManager.class)); // read initial value of ringer mode mStoredRingerMode = audioManager.getRingerMode(); Loading @@ -206,25 +205,20 @@ public class TbsGeneric { mTbsGatt.clearSilentModeFlag(); } mReceiver = new Receiver(); IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mTbsGatt.getContext().registerReceiver(mReceiver, filter); mContext.registerReceiver(mReceiver, filter); mIsInitialized = true; return true; } public synchronized void cleanup() { Log.d(TAG, "cleanup"); if (mTbsGatt != null) { if (mReceiver != null) { mTbsGatt.getContext().unregisterReceiver(mReceiver); if (mIsInitialized) { mContext.unregisterReceiver(mReceiver); } mTbsGatt.cleanup(); mTbsGatt = null; } mIsInitialized = false; } Loading @@ -236,10 +230,8 @@ public class TbsGeneric { */ public synchronized void onDeviceAuthorizationSet(BluetoothDevice device) { // Notify TBS GATT service instance in case of pending operations if (mTbsGatt != null) { mTbsGatt.onDeviceAuthorizationSet(device); } } /** * Set inband ringtone for the device. When set, notification will be sent to given device. Loading @@ -247,10 +239,6 @@ public class TbsGeneric { * @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); } Loading @@ -260,10 +248,6 @@ public class TbsGeneric { * @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); } Loading Loading @@ -767,7 +751,7 @@ public class TbsGeneric { Log.i(TAG, "originate uri=" + uri); Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mTbsGatt.getContext().startActivity(intent); mContext.startActivity(intent); mTbsGatt.setCallControlPointResult( device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, Loading
android/app/src/com/android/bluetooth/tbs/TbsService.java +12 −19 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package com.android.bluetooth.tbs; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static java.util.Objects.requireNonNull; import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeCall; Loading @@ -27,13 +29,13 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothLeCallControl; import android.bluetooth.IBluetoothLeCallControlCallback; import android.content.AttributionSource; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; import android.sysprop.BluetoothProperties; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.VisibleForTesting; Loading @@ -44,16 +46,20 @@ import java.util.Map; import java.util.UUID; public class TbsService extends ProfileService { private static final String TAG = "TbsService"; private static final String TAG = TbsService.class.getSimpleName(); private static TbsService sTbsService; private final Map<BluetoothDevice, Integer> mDeviceAuthorizations = new HashMap<>(); private final TbsGeneric mTbsGeneric; private final TbsGeneric mTbsGeneric = new TbsGeneric(); public TbsService(AdapterService adapterService) { super(requireNonNull(adapterService)); public TbsService(Context ctx) { super(ctx); // Mark service as started setTbsService(this); mTbsGeneric = new TbsGeneric(adapterService, new TbsGatt(adapterService, this)); } public static boolean isEnabled() { Loading @@ -65,19 +71,6 @@ public class TbsService extends ProfileService { return new TbsServerBinder(this); } @Override public void start() { Log.d(TAG, "start()"); if (sTbsService != null) { throw new IllegalStateException("start() called twice"); } // Mark service as started setTbsService(this); mTbsGeneric.init(new TbsGatt(this)); } @Override public void stop() { Log.d(TAG, "stop()"); Loading