blob: ebcbf81b541a79ee03421157f76918316f4aa71c [file] [log] [blame]
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.bluetooth.bc;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import android.app.ActivityManager;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothSyncHelper;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBleBroadcastAudioScanAssistCallback;
import android.bluetooth.BluetoothSyncHelper;
import android.bluetooth.BleBroadcastSourceInfo;
import android.bluetooth.BleBroadcastSourceChannel;
import android.bluetooth.BleBroadcastAudioScanAssistManager;
import android.bluetooth.BleBroadcastAudioScanAssistCallback;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.HandlerThread;
import android.util.Log;
import android.os.ParcelUuid;
import android.bluetooth.BluetoothUuid;
import java.util.ArrayList;
import android.os.ServiceManager;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.PeriodicAdvertisingCallback;
import android.bluetooth.le.PeriodicAdvertisingManager;
import android.bluetooth.le.PeriodicAdvertisingReport;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import com.android.bluetooth.BluetoothMetricsProto;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.btservice.ProfileService;
import com.android.internal.annotations.VisibleForTesting;
import com.android.bluetooth.btservice.AdapterService;
//*_CSIP
//CSIP related imports
import com.android.bluetooth.groupclient.GroupService;
import android.bluetooth.BluetoothGroupCallback;
import android.bluetooth.DeviceGroup;
//_CSIP*/
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.UUID;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.NoSuchElementException;
import android.os.SystemProperties;
import com.android.internal.util.ArrayUtils;
/** @hide */
public class BCService extends ProfileService {
private static final boolean DBG = true;
private static final String TAG = BCService.class.getSimpleName();
private static final ParcelUuid CAS_UUID = null;//ParcelUuid.fromString("000089FF-0000-1000-8000-00805F9B34FB");
public static final String BC_ID = "0000184F-0000-1000-8000-00805F9B34FB";
public static final String BS_ID = "00001852-0000-1000-8000-00805F9B34FB";
private static BCService sBCService;
private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10;
private static final int MAX_BASS_CLIENT_CSET_MEMBERS = 10;
private final Map<BluetoothDevice, BassClientStateMachine> mStateMachines =
new HashMap<>();
private HandlerThread mStateMachinesThread;
private final Map<Integer, BassCsetManager> mSetManagers =
new HashMap<>();
private HandlerThread mSetManagerThread;
private AdapterService mAdapterService;
private Map<BluetoothDevice, ArrayList<IBleBroadcastAudioScanAssistCallback>> mAppCallbackMap =
new HashMap<BluetoothDevice, ArrayList<IBleBroadcastAudioScanAssistCallback>>();
private BassUtils bassUtils = null;
public static final int INVALID_SYNC_HANDLE = -1;
public static final int INVALID_ADV_SID = -1;
public static final int INVALID_ADV_ADDRESS_TYPE = -1;
public static final int INVALID_ADV_INTERVAL = -1;
public static final int INVALID_BROADCAST_ID = -1;
private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
//*_CSIP
//CSET interfaces
private GroupService mSetCoordinator = GroupService.getGroupService();
public int mCsipAppId = -1;
private int mQueuedOps = 0;
//_CSIP*/
/*Caching the PAresults from Broadcast source*/
/*This is stored at service so that each device state machine can access
and use it as needed. Once the periodic sync in cancelled, this data will bre
removed to ensure stable data won't used*/
/*broadcastSrcDevice, syncHandle*/
private Map<BluetoothDevice, Integer> mSyncHandleMap;
/*syncHandle, parsed BaseData data*/
private Map<Integer, BaseData> mSyncHandleVsBaseInfo;
/*bcastSrcDevice, corresponding PAResultsMap*/
private Map<BluetoothDevice, PAResults> mPAResultsMap;
public class PAResults {
public BluetoothDevice mDevice;
public int mAddressType;
public int mAdvSid;
public int mSyncHandle;
public byte metaDataLength;
public byte[] metaData;
public int mPAInterval;
public int mBroadcastId;
PAResults(BluetoothDevice device, int addressType,
int syncHandle, int advSid, int paInterval, int broadcastId) {
mDevice = device;
mAddressType = addressType;
mAdvSid = advSid;
mSyncHandle = syncHandle;
mPAInterval = paInterval;
mBroadcastId = broadcastId;
}
public void updateSyncHandle(int syncHandle) {
mSyncHandle = syncHandle;
}
public void updateAdvSid(int advSid) {
mAdvSid = advSid;
}
public void updateAddressType(int addressType) {
mAddressType = addressType;
}
public void updateAdvInterval(int advInterval) {
mPAInterval = advInterval;
}
public void updateBroadcastId(int broadcastId) {
mBroadcastId = broadcastId;
}
public void print() {
log("-- PAResults --");
log("mDevice:" + mDevice);
log("mAddressType:" + mAddressType);
log("mAdvSid:" + mAdvSid);
log("mSyncHandle:" + mSyncHandle);
log("mPAInterval:" + mPAInterval);
log("mBroadcastId:" + mBroadcastId);
log("-- END: PAResults --");
}
};
public void updatePAResultsMap(BluetoothDevice device, int addressType, int syncHandle, int advSid, int advInterval, int bId) {
log("updatePAResultsMap: device: " + device);
log("updatePAResultsMap: syncHandle: " + syncHandle);
log("updatePAResultsMap: advSid: " + advSid);
log("updatePAResultsMap: addressType: " + addressType);
log("updatePAResultsMap: advInterval: " + advInterval);
log("updatePAResultsMap: broadcastId: " + bId);
log("mSyncHandleMap" + mSyncHandleMap);
log("mPAResultsMap" + mPAResultsMap);
//Cache the SyncHandle
if (mSyncHandleMap != null) {
Integer i = new Integer(syncHandle);
mSyncHandleMap.put(device, i);
}
if (mPAResultsMap != null) {
PAResults paRes = mPAResultsMap.get(device);
if (paRes == null) {
log("PAResmap: add >>>");
paRes = new PAResults (device, addressType,
syncHandle, advSid, advInterval, bId);
if (paRes != null) {
paRes.print();
mPAResultsMap.put(device, paRes);
}
} else {
if (advSid != INVALID_ADV_SID) {
paRes.updateAdvSid(advSid);
}
if (syncHandle != INVALID_SYNC_HANDLE) {
paRes.updateSyncHandle(syncHandle);
}
if (addressType != INVALID_ADV_ADDRESS_TYPE) {
paRes.updateAddressType(addressType);
}
if (advInterval != INVALID_ADV_INTERVAL) {
paRes.updateAdvInterval(advInterval);
}
if (bId != INVALID_BROADCAST_ID) {
paRes.updateBroadcastId(bId);
}
log("PAResmap: update >>>");
paRes.print();
mPAResultsMap.replace(device, paRes);
}
}
log(">>mPAResultsMap" + mPAResultsMap);
}
public PAResults getPAResults(BluetoothDevice device) {
PAResults res = null;
if (mPAResultsMap != null) {
res = mPAResultsMap.get(device);
} else {
Log.e(TAG, "getPAResults: mPAResultsMap is null");
}
return res;
}
public PAResults clearPAResults(BluetoothDevice device) {
PAResults res = null;
if (mPAResultsMap != null) {
res = mPAResultsMap.remove(device);
} else {
Log.e(TAG, "getPAResults: mPAResultsMap is null");
}
return res;
}
public void updateBASE(int syncHandlemap, BaseData base) {
if (mSyncHandleVsBaseInfo != null) {
log("updateBASE : mSyncHandleVsBaseInfo>>");
mSyncHandleVsBaseInfo.put(syncHandlemap, base);
} else {
Log.e(TAG, "updateBASE: mSyncHandleVsBaseInfo is null");
}
}
public BaseData getBASE(int syncHandlemap) {
BaseData base = null;
if (mSyncHandleVsBaseInfo != null) {
log("getBASE : syncHandlemap::" + syncHandlemap);
base = mSyncHandleVsBaseInfo.get(syncHandlemap);
} else {
Log.e(TAG, "getBASE: mSyncHandleVsBaseInfo is null");
}
log("getBASE returns" + base);
return base;
}
public void clearBASE(int syncHandlemap) {
if (mSyncHandleVsBaseInfo != null) {
log("clearBASE : mSyncHandleVsBaseInfo>>");
mSyncHandleVsBaseInfo.remove(syncHandlemap);
} else {
Log.e(TAG, "updateBASE: mSyncHandleVsBaseInfo is null");
}
}
public void setActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
log("setActiveSyncedSource: scanDelegator" + scanDelegator + ":: sourceDevice:" + sourceDevice);
if (sourceDevice == null) {
mActiveSourceMap.remove(scanDelegator);
} else {
mActiveSourceMap.put(scanDelegator, sourceDevice);
}
}
public BluetoothDevice getActiveSyncedSource(BluetoothDevice scanDelegator) {
BluetoothDevice currentSource = mActiveSourceMap.get(scanDelegator);
log("getActiveSyncedSource: scanDelegator" + scanDelegator + "returning " + currentSource);
return currentSource;
}
@Override
protected IProfileServiceBinder initBinder() {
return new BluetoothSyncHelperBinder(this);
}
//*_CSIP
private BluetoothGroupCallback mBluetoothGroupCallback = new BluetoothGroupCallback() {
public void onGroupClientAppRegistered(int status, int appId) {
log("onCsipAppRegistered:" + status + "appId: " + appId);
if (status == 0) {
mCsipAppId = appId;
} else {
Log.e(TAG, "Csip registeration failed, status:" + status);
}
}
public void onConnectionStateChanged (int state, BluetoothDevice device) {
log("onConnectionStateChanged: Device: " + device + "state: " + state);
//notify the statemachine about CSIP connection
synchronized (mStateMachines) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
Message m = stateMachine.obtainMessage(BassClientStateMachine.CSIP_CONNECTION_STATE_CHANGED);
m.obj = state;
stateMachine.sendMessage(m);
}
}
public void onNewGroupFound (int setId, BluetoothDevice device, UUID uuid) { }
public void onGroupDiscoveryStatusChanged (int setId, int status, int reason) { }
public void onGroupDeviceFound (int setId, BluetoothDevice device) { }
public void onExclusiveAccessChanged (int setId, int value, int status, List<BluetoothDevice> devices) {
log("onLockStatusChanged: setId" + setId + devices + "status:" + status);
BassCsetManager setMgr = null;
setMgr = getOrCreateCSetManager(setId, null);
if (setMgr == null) {
return;
}
log ("sending Lock status to setId:" + setId);
Message m = setMgr.obtainMessage(BassCsetManager.LOCK_STATE_CHANGED);
m.obj = devices;
m.arg1 = value;
setMgr.sendMessage(m);
}
public void onExclusiveAccessStatusFetched (int setId, int lockStatus) { }
public void onExclusiveAccessAvailable (int setId, BluetoothDevice device) { }
};
//_CSIP*/
@Override
protected boolean start() {
if (DBG) {
Log.d(TAG, "start()");
}
mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
"AdapterService cannot be null when BCService starts");
mStateMachines.clear();
mStateMachinesThread = new HandlerThread("BCService.StateMachines");
mStateMachinesThread.start();
mSetManagers.clear();
mSetManagerThread = new HandlerThread("BCService.SetManagers");
mSetManagerThread.start();
setBCService(this);
bassUtils = new BassUtils(this);
//Saving PSync stuff for future addition
mSyncHandleMap = new HashMap<BluetoothDevice, Integer>();
mPAResultsMap = new HashMap<BluetoothDevice, PAResults>();
mSyncHandleVsBaseInfo = new HashMap<Integer, BaseData>();
mActiveSourceMap = new HashMap<BluetoothDevice, BluetoothDevice>();
//*_CSIP
//CSET initialization
mSetCoordinator = GroupService.getGroupService();
if (mSetCoordinator != null) {
mSetCoordinator.registerGroupClientModule(mBluetoothGroupCallback);
}
//_CSIP*/
/*_PACS
mPacsClientService = PacsClientService.getPacsClientService();
_PACS*/
///*_GAP
//GAP registeration for Bass UUID notification
if (mAdapterService != null) {
log("register for BASS UUID notif");
ParcelUuid bassUuid = new ParcelUuid(BassClientStateMachine.BASS_UUID);
mAdapterService.registerUuidSrvcDisc(bassUuid);
}
//_GAP*/
return true;
}
@Override
protected boolean stop() {
if (DBG) {
Log.d(TAG, "stop()");
}
synchronized (mStateMachines) {
for (BassClientStateMachine sm : mStateMachines.values()) {
sm.doQuit();
sm.cleanup();
}
mStateMachines.clear();
}
if (mStateMachinesThread != null) {
mStateMachinesThread.quitSafely();
mStateMachinesThread = null;
}
if (mSetManagerThread != null) {
mSetManagerThread.quitSafely();
mSetManagerThread = null;
}
setBCService(null);
if (mAppCallbackMap != null) {
mAppCallbackMap.clear();
mAppCallbackMap = null;
}
if (mSyncHandleMap != null) {
mSyncHandleMap.clear();
mSyncHandleMap = null;
}
if (mActiveSourceMap != null) {
mActiveSourceMap.clear();
mActiveSourceMap = null;
}
//*_CSIP
if (mSetCoordinator != null && mCsipAppId != -1) {
//mSetCoordinator.unregisterGroupClientModule(mCsipAppId);
}
//_CSIP*/
return true;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "Need to unregister app");
//unregisterApp();
return super.onUnbind(intent);
}
/**
* Get the BCService instance
* @return BCService instance
*/
public static synchronized BCService getBCService() {
if (sBCService == null) {
Log.w(TAG, "getBCService(): service is NULL");
return null;
}
if (!sBCService.isAvailable()) {
Log.w(TAG, "getBCService(): service is not available");
return null;
}
return sBCService;
}
public BassUtils getBassUtils() {
return bassUtils;
}
public BluetoothDevice getDeviceForSyncHandle(int syncHandle) {
BluetoothDevice dev = null;
if (mSyncHandleMap != null) {
for (Map.Entry<BluetoothDevice, Integer> entry : mSyncHandleMap.entrySet()) {
Integer value = entry.getValue();
if (value == syncHandle) {
dev = entry.getKey();
}
}
}
return dev;
}
private static synchronized void setBCService(BCService instance) {
if (DBG) {
Log.d(TAG, "setBCService(): set to: " + instance);
}
sBCService = instance;
}
/**
* Connects the bass profile to the passed in device
*
* @param device is the device with which we will connect the Bass profile
* @return true if BAss profile successfully connected, false otherwise
*/
public boolean connect(BluetoothDevice device) {
if (DBG) {
Log.d(TAG, "connect(): " + device);
}
if (device == null) {
return false;
}
if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_UNKNOWN) {
return false;
}
synchronized (mStateMachines) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
stateMachine.sendMessage(BassClientStateMachine.CONNECT);
}
return true;
}
/**
* Disconnects Bassclient profile for the passed in device
*
* @param device is the device with which we want to disconnected the BAss client profile
* @return true if Bass client profile successfully disconnected, false otherwise
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) {
Log.d(TAG, "disconnect(): " + device);
}
if (device == null) {
return false;
}
synchronized (mStateMachines) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
stateMachine.sendMessage(BassClientStateMachine.DISCONNECT);
}
return true;
}
List<BluetoothDevice> getConnectedDevices() {
synchronized (mStateMachines) {
List<BluetoothDevice> devices = new ArrayList<>();
for (BassClientStateMachine sm : mStateMachines.values()) {
if (sm.isConnected()) {
devices.add(sm.getDevice());
}
}
log("getConnectedDevices: " + devices);
return devices;
}
}
/**
* Check whether can connect to a peer device.
* The check considers a number of factors during the evaluation.
*
* @param device the peer device to connect to
* @return true if connection is allowed, otherwise false
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public boolean okToConnect(BluetoothDevice device) {
// Check if this is an incoming connection in Quiet mode.
if (mAdapterService.isQuietModeEnabled()) {
Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
return false;
}
// Check connection policy and accept or reject the connection.
int connectionPolicy = getConnectionPolicy(device);
int bondState = mAdapterService.getBondState(device);
// Allow this connection only if the device is bonded. Any attempt to connect while
// bonding would potentially lead to an unauthorized connection.
if (bondState != BluetoothDevice.BOND_BONDED) {
Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
return false;
} else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
&& connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
// Otherwise, reject the connection if connectionPolicy is not valid.
Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy);
return false;
}
return true;
}
List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
ArrayList<BluetoothDevice> devices = new ArrayList<>();
if (states == null) {
return devices;
}
final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
if (bondedDevices == null) {
return devices;
}
synchronized (mStateMachines) {
for (BluetoothDevice device : bondedDevices) {
final ParcelUuid[] featureUuids = device.getUuids();
if (!ArrayUtils.contains(featureUuids, new ParcelUuid(BassClientStateMachine.BASS_UUID))) {
continue;
}
int connectionState = BluetoothProfile.STATE_DISCONNECTED;
BassClientStateMachine sm = getOrCreateStateMachine(device);
if (sm != null) {
connectionState = sm.getConnectionState();
}
for (int state : states) {
if (connectionState == state) {
devices.add(device);
break;
}
}
}
return devices;
}
}
public int getConnectionState(BluetoothDevice device) {
synchronized (mStateMachines) {
BassClientStateMachine sm = getOrCreateStateMachine(device);
if (sm == null) {
log("getConnectionState returns STATE_DISC");
return BluetoothProfile.STATE_DISCONNECTED;
}
return sm.getConnectionState();
}
}
/**
* Set the connectionPolicy of the Hearing Aid profile.
*
* @param device the remote device
* @param connectionPolicy the connection policy of the profile
* @return true on success, otherwise false
*/
public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
if (DBG) {
Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
}
boolean setSuccessfully;
setSuccessfully = mAdapterService.getDatabase()
.setProfileConnectionPolicy(device, BluetoothProfile.BC_PROFILE, connectionPolicy);
if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
connect(device);
} else if (setSuccessfully
&& connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
disconnect(device);
}
return setSuccessfully;
}
/**
* Get the connection policy of the profile.
*
* <p> The connection policy can be any of:
* {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
* {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
* {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
*
* @param device Bluetooth device
* @return connection policy of the device
* @hide
*/
public int getConnectionPolicy(BluetoothDevice device) {
return mAdapterService.getDatabase()
.getProfileConnectionPolicy(device, BluetoothProfile.BC_PROFILE);
}
public void sendBroadcastSourceSelectedCallback(BluetoothDevice device, List<BleBroadcastSourceChannel> bChannels, int status){
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return;
}
for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
try {
cb.onBleBroadcastAudioSourceSelected(device, status, bChannels);
} catch (RemoteException e) {
Log.e(TAG, "Exception while calling sendBroadcastSourceSelectedCallback");
}
}
}
public void sendAddBroadcastSourceCallback(BluetoothDevice device, byte srcId, int status){
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return;
}
for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
try {
cb.onBleBroadcastAudioSourceAdded(device, srcId, status);
} catch (RemoteException e) {
Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceAdded");
}
}
}
public void sendUpdateBroadcastSourceCallback(BluetoothDevice device, byte sourceId, int status){
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return;
}
for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
try {
cb.onBleBroadcastAudioSourceUpdated(device, sourceId, status);
} catch (RemoteException e) {
Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceUpdated");
}
}
}
public void sendRemoveBroadcastSourceCallback(BluetoothDevice device, byte sourceId, int status){
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return;
}
for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
try {
cb.onBleBroadcastAudioSourceRemoved(device, sourceId, status);
} catch (RemoteException e) {
Log.e(TAG, "Exception while calling onBleBroadcastAudioSourceRemoved");
}
}
}
public void sendSetBroadcastPINupdatedCallback(BluetoothDevice device, byte sourceId, int status){
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return;
}
for (IBleBroadcastAudioScanAssistCallback cb : cbs) {
try {
cb.onBleBroadcastPinUpdated(device, sourceId, status);
} catch (RemoteException e) {
Log.e(TAG, "Exception while calling onBleBroadcastPinUpdated");
}
}
}
public void registerAppCallback (BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
Log.i(TAG, "registerAppCallback" + device);
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.i(TAG, "registerAppCallback: entry exists");
cbs = new ArrayList<IBleBroadcastAudioScanAssistCallback>();
}
cbs.add(cb);
mAppCallbackMap.put(device, cbs);
return;
}
public void unregisterAppCallback (BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
Log.i(TAG, "unregisterAppCallback" + device);
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.i(TAG, "unregisterAppCallback: cb list is null");
return;
} else {
boolean ret = cbs.remove(cb);
Log.i(TAG, "unregisterAppCallback: ret value of removal from list:" + ret);
}
if (cbs.size() != 0) {
mAppCallbackMap.replace(device, cbs);
} else {
Log.i(TAG, "unregisterAppCallback: Remove the cmplete entry");
mAppCallbackMap.remove(device);
}
return;
}
public boolean searchforLeAudioBroadcasters (BluetoothDevice device) {
Log.i(TAG, "searchforLeAudioBroadcasters on behalf of" + device);
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
return false;
}
boolean ret = false;
if (bassUtils != null) {
ret = bassUtils.searchforLeAudioBroadcasters(device, cbs);
} else {
Log.e(TAG, "searchforLeAudioBroadcasters :Null Bass Util Handle" + device);
ret = false;
}
return ret;
}
public boolean stopSearchforLeAudioBroadcasters (BluetoothDevice device) {
Log.i(TAG, "stopsearchforLeAudioBroadcasters on behalf of" + device);
ArrayList<IBleBroadcastAudioScanAssistCallback> cbs = mAppCallbackMap.get(device);
if (cbs == null) {
Log.e(TAG, "no App callback for this device" + device);
}
boolean ret = false;
if (bassUtils != null) {
ret = bassUtils.stopSearchforLeAudioBroadcasters(device, cbs);
} else {
Log.e(TAG, "stopsearchforLeAudioBroadcasters :Null Bass Util Handle" + device);
ret = false;
}
return ret;
}
public boolean selectBroadcastSource (BluetoothDevice device, ScanResult scanRes, boolean isGroupOp, boolean auto) {
Log.i(TAG, "selectBroadcastSource for " + device + "isGroupOp:" + isGroupOp);
Log.i(TAG, "ScanResult " + scanRes);
if (scanRes == null) {
Log.e(TAG, "selectBroadcastSource: null Scan results");
return false;
}
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
if (isRoomForBroadcastSourceAddition(listOfDevices) == false) {
sendBroadcastSourceSelectedCallback(device, null,
BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT);
return false;
}
//dummy BleSourceInfo from scanRes
BleBroadcastSourceInfo scanResSI = new BleBroadcastSourceInfo (scanRes.getDevice(),
BassClientStateMachine.INVALID_SRC_ID,
(byte)scanRes.getAdvertisingSid(),
BleBroadcastSourceInfo.BROADCASTER_ID_INVALID,
scanRes.getAddressType(),
BleBroadcastSourceInfo.BROADCAST_ASSIST_PA_SYNC_STATE_INVALID,
BleBroadcastSourceInfo.BROADCAST_ASSIST_ENC_STATE_INVALID,
null,
(byte)0,
BleBroadcastSourceInfo.BROADCAST_ASSIST_AUDIO_SYNC_STATE_NOT_SYNCHRONIZED,
null,
null);
if (isValidBroadcastSourceAddition(listOfDevices, scanResSI) == false) {
sendBroadcastSourceSelectedCallback(device,
null, BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION);
return false;
}
startScanOffloadInternal(device, isGroupOp);
synchronized (mStateMachines) {
BassClientStateMachine sm = getOrCreateStateMachine(device);
if (sm == null) {
return false;
}
Message m = sm.obtainMessage(BassClientStateMachine.SELECT_BCAST_SOURCE);
m.obj = scanRes;
if (auto) {
m.arg1 = sm.AUTO;
} else {
m.arg1 = sm.USER;
}
if (isGroupOp) {
m.arg2 = sm.GROUP_OP;
} else {
m.arg2 = sm.NON_GROUP_OP;
}
sm.sendMessage(m);
}
return true;
}
public synchronized void notifyOperationCompletion(BluetoothDevice device, int pendingOperation) {
log("notifyOperationCompletion: " + device + "pendingOperation: " +
BassClientStateMachine.messageWhatToString(pendingOperation));
//synchronized (mStateMachines) {
switch (pendingOperation) {
case BassClientStateMachine.START_SCAN_OFFLOAD:
case BassClientStateMachine.STOP_SCAN_OFFLOAD:
case BassClientStateMachine.ADD_BCAST_SOURCE:
case BassClientStateMachine.UPDATE_BCAST_SOURCE:
case BassClientStateMachine.REMOVE_BCAST_SOURCE:
case BassClientStateMachine.SET_BCAST_CODE:
if (mQueuedOps > 0) {
mQueuedOps = mQueuedOps - 1;
} else {
log("not a queued op, Internal op");
return;
}
break;
default:
{
log("notifyOperationCompletion: unhandled case");
return;
}
}
//}
if (mQueuedOps == 0) {
log("notifyOperationCompletion: all ops are done!");
//trigger unlock with last device
triggerUnlockforCSet(device);
}
}
public synchronized boolean startScanOffload (BluetoothDevice masterDevice, List<BluetoothDevice> devices) {
Log.i(TAG, "startScanOffload for " + devices);
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
continue;
}
stateMachine.sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
public synchronized boolean stopScanOffload (BluetoothDevice masterDevice, List<BluetoothDevice> devices) {
Log.i(TAG, "stopScanOffload for " + devices);
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
continue;
}
stateMachine.sendMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
public boolean isLocalBroadcasting() {
return bassUtils.isLocalLEAudioBroadcasting();
}
private boolean isValidBroadcastSourceAddition(List<BluetoothDevice> devices,
BleBroadcastSourceInfo srcInfo) {
boolean ret = true;
//run through all the device, if it is not valid
//to even one device to add this source, return failure
for (BluetoothDevice dev : devices) {
List<BleBroadcastSourceInfo> currentSourceInfos =
getAllBroadcastSourceInformation(dev);
if (currentSourceInfos == null) {
log("currentSourceInfos is null for " + dev);
continue;
}
for (int i=0; i<currentSourceInfos.size(); i++) {
if (srcInfo.matches(currentSourceInfos.get(i))) {
ret = false;
Log.e(TAG, "isValidBroadcastSourceAddition: fails for: " + dev + "&srcInfo" + srcInfo);
break;
}
}
}
log("isValidBroadcastSourceInfo returns: " + ret);
return ret;
}
private boolean isRoomForBroadcastSourceAddition(List<BluetoothDevice> devices) {
boolean isRoomAvail = false;
//run through all the device, if it is not valid
//to even one device to add this source, return failure
for (BluetoothDevice dev : devices) {
isRoomAvail = false;
List<BleBroadcastSourceInfo> currentSourceInfos =
getAllBroadcastSourceInformation(dev);
for (int i=0; i<currentSourceInfos.size(); i++) {
BleBroadcastSourceInfo srcInfo = currentSourceInfos.get(i);
if (srcInfo.isEmptyEntry()) {
isRoomAvail = true;
continue;
}
}
if (isRoomAvail == false) {
Log.e(TAG, "isRoomForBroadcastSourceAddition: fails for: " + dev);
break;
}
}
log("isRoomForBroadcastSourceAddition returns: " + isRoomAvail);
return isRoomAvail;
}
public synchronized boolean addBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices, BleBroadcastSourceInfo srcInfo
) {
Log.i(TAG, "addBroadcastSource for " + devices +
"SourceInfo " + srcInfo);
if (srcInfo == null) {
Log.e(TAG, "addBroadcastSource: null SrcInfo");
return false;
}
if (isRoomForBroadcastSourceAddition(devices) == false) {
sendAddBroadcastSourceCallback(masterDevice,
BassClientStateMachine.INVALID_SRC_ID, BleBroadcastAudioScanAssistCallback.BASS_STATUS_NO_EMPTY_SLOT);
triggerUnlockforCSet(masterDevice);
return false;
}
if (isValidBroadcastSourceAddition(devices, srcInfo) == false) {
sendAddBroadcastSourceCallback(masterDevice,
BassClientStateMachine.INVALID_SRC_ID, BleBroadcastAudioScanAssistCallback.BASS_STATUS_DUPLICATE_ADDITION);
triggerUnlockforCSet(masterDevice);
return false;
}
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
Log.w(TAG, "addBroadcastSource: device seem to be not avaiable, proceed");
continue;
}
Message m = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE);
m.obj = srcInfo;
stateMachine.sendMessage(m);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
private byte getSrcIdForCSMember(BluetoothDevice masterDevice, BluetoothDevice memberDevice, byte masterSrcId) {
byte targetSrcId = -1;
List<BleBroadcastSourceInfo> masterSrcInfos = getAllBroadcastSourceInformation(masterDevice);
List<BleBroadcastSourceInfo> memberSrcInfos = getAllBroadcastSourceInformation(memberDevice);
if (masterSrcInfos == null || masterSrcInfos.size() == 0 ||
memberSrcInfos == null || memberSrcInfos.size() == 0) {
Log.e(TAG, "master or member source Infos not available");
return targetSrcId;
}
if (masterDevice.equals(memberDevice)) {
log("master: " + masterDevice + "member:memberDevice");
return masterSrcId;
}
BluetoothDevice masterSrcDevice = null;
for (int i=0; i<masterSrcInfos.size(); i++) {
if (masterSrcInfos.get(i).getSourceId() == masterSrcId) {
masterSrcDevice = masterSrcInfos.get(i).getSourceDevice();
break;
}
}
if (masterSrcDevice == null) {
Log.e(TAG, "No matching SRC Id for the operation in masterDevice");
return targetSrcId;
}
//look for this srcAddress in member to retrieve the srcId
for (int i=0; i<memberSrcInfos.size(); i++) {
if (masterSrcDevice.equals(memberSrcInfos.get(i).getSourceDevice())) {
targetSrcId = masterSrcInfos.get(i).getSourceId();
break;
}
}
if (targetSrcId == -1) {
Log.e(TAG, "No matching SRC Address in the member Src Infos");
}
return targetSrcId;
}
public synchronized boolean updateBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
BleBroadcastSourceInfo srcInfo
) {
int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
Log.i(TAG, "updateBroadcastSource for " + devices +
"masterDevice " + masterDevice +
"SourceInfo " + srcInfo);
if (srcInfo == null) {
Log.e(TAG, "updateBroadcastSource: null SrcInfo");
return false;
}
for (BluetoothDevice dev : devices) {
if (getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId()) == -1) {
if (devices.size() > 1) {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
} else {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
}
sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
status);
triggerUnlockforCSet(masterDevice);
return false;
}
}
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
Log.w(TAG, "updateBroadcastSource: Device seem to be not avaiable");
continue;
}
byte targetSrcId = getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId());
srcInfo.setSourceId(targetSrcId);
Message m = stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
m.obj = srcInfo;
m.arg1 = stateMachine.USER;
stateMachine.sendMessage(m);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
public synchronized boolean setBroadcastCode (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
BleBroadcastSourceInfo srcInfo
) {
int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
Log.i(TAG, "setBroadcastCode for " + devices +
"masterDevice" + masterDevice +
"Broadcast PIN" + srcInfo.getBroadcastCode());
if (srcInfo == null) {
Log.e(TAG, "setBroadcastCode: null SrcInfo");
return false;
}
for (BluetoothDevice dev : devices) {
if (getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId()) == -1) {
if (devices.size() > 1) {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
} else {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
}
sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
status);
triggerUnlockforCSet(masterDevice);
return false;
}
}
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
Log.w(TAG, "setBroadcastCode: Device seem to be not avaiable");
continue;
}
byte targetSrcId = getSrcIdForCSMember(masterDevice, dev, srcInfo.getSourceId());
srcInfo.setSourceId(targetSrcId);
Message m = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
m.obj = srcInfo;
m.arg1 = stateMachine.FRESH;
stateMachine.sendMessage(m);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
public synchronized boolean removeBroadcastSource (BluetoothDevice masterDevice, List<BluetoothDevice> devices,
byte sourceId
) {
int status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
Log.i(TAG, "removeBroadcastSource for " + devices +
"masterDevice " + masterDevice +
"removeBroadcastSource: sourceId:" + sourceId);
if (sourceId == BassClientStateMachine.INVALID_SRC_ID) {
Log.e(TAG, "removeBroadcastSource: Invalid source Id");
return false;
}
for (BluetoothDevice dev : devices) {
if (getSrcIdForCSMember(masterDevice, dev, sourceId) == -1) {
if (devices.size() > 1) {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_GROUP_OP;
} else {
status = BleBroadcastAudioScanAssistCallback.BASS_STATUS_INVALID_SOURCE_ID;
}
sendRemoveBroadcastSourceCallback(masterDevice, BassClientStateMachine.INVALID_SRC_ID,
status);
triggerUnlockforCSet(masterDevice);
return false;
}
}
for (BluetoothDevice dev : devices) {
BassClientStateMachine stateMachine = getOrCreateStateMachine(dev);
if (stateMachine == null) {
Log.w(TAG, "setBroadcastCode: Device seem to be not avaiable");
continue;
}
Message m = stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE);
m.arg1 = getSrcIdForCSMember(masterDevice, dev, sourceId);
log("removeBroadcastSource: send message to SM " + dev);
stateMachine.sendMessage(m);
mQueuedOps = mQueuedOps + 1;
}
return true;
}
void triggerUnlockforCSet (BluetoothDevice device) {
//get setId
int setId = getCsetId(device);
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
Log.e(TAG, "triggerUnlockforCSet: setMgr is NULL");
return;
}
//Sending UnLock to
log ("sending Unlock to device:" + device);
Message m = setMgr.obtainMessage(BassCsetManager.UNLOCK);
setMgr.sendMessage(m);
}
public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation (BluetoothDevice device
) {
Log.i(TAG, "getAllBroadcastSourceInformation for " + device);
synchronized (mStateMachines) {
BassClientStateMachine sm = getOrCreateStateMachine(device);
if (sm == null) {
return null;
}
return sm.getAllBroadcastSourceInformation();
}
}
private BassClientStateMachine getOrCreateStateMachine(BluetoothDevice device) {
if (device == null) {
Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
return null;
}
synchronized (mStateMachines) {
BassClientStateMachine sm = mStateMachines.get(device);
if (sm != null) {
return sm;
}
// Limit the maximum number of state machines to avoid DoS attack
if (mStateMachines.size() >= MAX_BASS_CLIENT_STATE_MACHINES) {
Log.e(TAG, "Maximum number of Bassclient state machines reached: "
+ MAX_BASS_CLIENT_STATE_MACHINES);
return null;
}
if (DBG) {
Log.d(TAG, "Creating a new state machine for " + device);
}
sm = BassClientStateMachine.make(device, this,
mStateMachinesThread.getLooper());
mStateMachines.put(device, sm);
return sm;
}
}
private BassCsetManager getOrCreateCSetManager(int setId, BluetoothDevice masterDevice) {
if (setId == -1) {
Log.e(TAG, "getOrCreateCSetManager failed: invalid setId");
return null;
}
synchronized (mSetManagers) {
BassCsetManager sm = mSetManagers.get(setId);
log("getOrCreateCSetManager: hashmap Entry:" + sm);
if (sm != null) {
return sm;
}
// Limit the maximum number of set manager state machines
if (mStateMachines.size() >= MAX_BASS_CLIENT_CSET_MEMBERS) {
Log.e(TAG, "Maximum number of Bassclient cset members reached: "
+ MAX_BASS_CLIENT_CSET_MEMBERS);
return null;
}
if (DBG) {
Log.d(TAG, "Creating a new set Manager for " + setId);
}
sm = BassCsetManager.make(setId, masterDevice, this,
mSetManagerThread.getLooper());
mSetManagers.put(setId, sm);
return sm;
}
}
public boolean isLockSupportAvailable(BluetoothDevice device) {
boolean isLockAvail = false;
boolean forceNoCsip = SystemProperties.getBoolean("persist.vendor.service.bt.forceNoCsip", false);
if (forceNoCsip) {
log("forceNoCsip is set");
return isLockAvail;
}
//*_CSIP
isLockAvail = mAdapterService.isGroupExclAccessSupport(device);
//_CSIP*/
log("isLockSupportAvailable for:" + device + "returns " + isLockAvail);
return isLockAvail;
}
private int getCsetId(BluetoothDevice device) {
int setId = 1;
//*_CSIP
setId = mSetCoordinator.getRemoteDeviceGroupId(device, CAS_UUID);
//_CSIP*/
log("getCsetId return:" + setId);
return setId;
}
public boolean stopScanOffloadInternal (BluetoothDevice device, boolean isGroupOp) {
boolean ret = false;
log("stopScanOffloadInternal: device: " + device
+ "isGroupOp" + isGroupOp);
/* Even If the request is for Grouoop, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp && isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_STOP_SCAN_OFFLOAD);
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = stopScanOffload(device, listOfDevices);
}
return ret;
}
public boolean startScanOffloadInternal (BluetoothDevice device, boolean isGroupOp) {
boolean ret = false;
log("startScanOffloadInternal: device: " + device
+ "isGroupOp" + isGroupOp);
/* Even If the request is for Grouoop, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp&& isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_START_SCAN_OFFLOAD);
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = startScanOffload(device, listOfDevices);
}
return ret;
}
public boolean addBroadcastSourceInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
boolean isGroupOp) {
boolean ret = false;
log("addBroadcastSourceInternal: device: " + device
+ "srcInfo" + srcInfo
+ "isGroupOp" + isGroupOp);
/* Even If the request is for Group, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp && isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_ADD_BCAST_SOURCE);
m.obj = srcInfo;
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = addBroadcastSource(device, listOfDevices, srcInfo);
}
return ret;
}
public boolean updateBroadcastSourceInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
boolean isGroupOp
) {
boolean ret = false;
log("updateBroadcastSourceInternal: device: " + device
+ "srcInfo" + srcInfo
+ "isGroupOp" + isGroupOp);
/* Even If the request is for Grouoop, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp && isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_UPDATE_BCAST_SOURCE);
m.obj = srcInfo;
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = updateBroadcastSource(device, listOfDevices, srcInfo);
}
return ret;
}
protected boolean setBroadcastCodeInternal (BluetoothDevice device, BleBroadcastSourceInfo srcInfo,
boolean isGroupOp
) {
boolean ret = false;
log("setBroadcastCodeInternal: device: " + device
+ "srcInfo" + srcInfo
+ "isGroupOp" + isGroupOp);
/* Even If the request is for Grouoop, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp && isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_SET_BCAST_CODE);
m.obj = srcInfo;
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = setBroadcastCode(device, listOfDevices, srcInfo);
}
return ret;
}
public boolean removeBroadcastSourceInternal (BluetoothDevice device, byte sourceId, boolean isGroupOp
) {
boolean ret = false;
/* Even If the request is for Grouoop, If Lock support not avaiable
* for that device, go ahead and treat this as single device operation
*/
if (isGroupOp && isLockSupportAvailable(device) == true) {
int setId = getCsetId(device);
synchronized (mSetManagers) {
BassCsetManager setMgr = getOrCreateCSetManager(setId, device);
if (setMgr == null) {
return false;
}
Message m = setMgr.obtainMessage(BassCsetManager.BASS_GRP_REMOVE_BCAST_SOURCE);
m.arg1 = sourceId;
setMgr.sendMessage(m);
//queue req and return true
ret = true;
}
} else {
List<BluetoothDevice> listOfDevices = new ArrayList<BluetoothDevice>();
listOfDevices.add(device);
ret = removeBroadcastSource(device, listOfDevices, sourceId);
}
return ret;
}
static void log(String msg) {
if (BassClientStateMachine.BASS_DBG) {
Log.d(TAG, msg);
}
}
/**
* Binder object: must be a static class or memory leak may occur
*/
@VisibleForTesting
static class BluetoothSyncHelperBinder extends IBluetoothSyncHelper.Stub
implements IProfileServiceBinder {
private BCService mService;
private BCService getService() {
if (!Utils.checkCallerIsSystemOrActiveUser(TAG)) {
return null;
}
if (mService != null && mService.isAvailable()) {
return mService;
}
return null;
}
BluetoothSyncHelperBinder(BCService svc) {
mService = svc;
}
@Override
public void cleanup() {
mService = null;
}
@Override
public boolean connect(BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return false;
}
return service.connect(device);
}
@Override
public boolean disconnect(BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return false;
}
return service.disconnect(device);
}
@Override
public List<BluetoothDevice> getConnectedDevices() {
BCService service = getService();
if (service == null) {
return new ArrayList<>();
}
return service.getConnectedDevices();
}
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
BCService service = getService();
if (service == null) {
return new ArrayList<>();
}
return service.getDevicesMatchingConnectionStates(states);
}
@Override
public int getConnectionState(BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return BluetoothProfile.STATE_DISCONNECTED;
}
return service.getConnectionState(device);
}
@Override
public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
BCService service = getService();
if (service == null) {
return false;
}
return service.setConnectionPolicy(device, connectionPolicy);
}
@Override
public int getConnectionPolicy(BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
}
return service.getConnectionPolicy(device);
}
@Override
public boolean searchforLeAudioBroadcasters (BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return false;
}
return service.searchforLeAudioBroadcasters(device);
}
@Override
public boolean stopSearchforLeAudioBroadcasters (BluetoothDevice device) {
BCService service = getService();
if (service == null) {
return false;
}
return service.stopSearchforLeAudioBroadcasters(device);
}
@Override
public boolean selectBroadcastSource (BluetoothDevice device, ScanResult scanRes, boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.selectBroadcastSource(device, scanRes, isGroupOp, false);
}
@Override
public void registerAppCallback(BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
BCService service = getService();
if (service == null) {
return;
}
service.registerAppCallback(device, cb);
}
@Override
public void unregisterAppCallback(BluetoothDevice device, IBleBroadcastAudioScanAssistCallback cb) {
BCService service = getService();
if (service == null) {
return;
}
service.unregisterAppCallback(device, cb);
}
@Override
public boolean startScanOffload(BluetoothDevice device, boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.startScanOffloadInternal(device, isGroupOp);
}
@Override
public boolean stopScanOffload(BluetoothDevice device, boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.stopScanOffloadInternal(device, isGroupOp);
}
@Override
public boolean addBroadcastSource(BluetoothDevice device, BleBroadcastSourceInfo srcInfo
, boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.addBroadcastSourceInternal(device, srcInfo, isGroupOp);
}
@Override
public boolean updateBroadcastSource (BluetoothDevice device,
BleBroadcastSourceInfo srcInfo,
boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.updateBroadcastSourceInternal(device, srcInfo, isGroupOp);
}
@Override
public boolean setBroadcastCode (BluetoothDevice device,
BleBroadcastSourceInfo srcInfo,
boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.setBroadcastCodeInternal(device, srcInfo, isGroupOp);
}
@Override
public boolean removeBroadcastSource (BluetoothDevice device,
byte sourceId,
boolean isGroupOp) {
BCService service = getService();
if (service == null) {
return false;
}
return service.removeBroadcastSourceInternal(device, sourceId, isGroupOp);
}
@Override
public List<BleBroadcastSourceInfo> getAllBroadcastSourceInformation (BluetoothDevice device
) {
BCService service = getService();
if (service == null) {
return null;
}
return service.getAllBroadcastSourceInformation(device);
}
}
}