1 /* 2 * Copyright 2021 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.settingslib.bluetooth; 19 20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; 21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 22 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothClass; 25 import android.bluetooth.BluetoothCsipSetCoordinator; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothProfile; 28 import android.content.Context; 29 import android.os.ParcelUuid; 30 import android.util.Log; 31 32 import androidx.annotation.RequiresApi; 33 34 import com.android.settingslib.R; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Map; 39 40 /** 41 * CSIP Set Coordinator handles Bluetooth CSIP Set Coordinator role profile. 42 */ 43 public class CsipSetCoordinatorProfile implements LocalBluetoothProfile { 44 private static final String TAG = "CsipSetCoordinatorProfile"; 45 private static final boolean VDBG = true; 46 47 private Context mContext; 48 49 private BluetoothCsipSetCoordinator mService; 50 private boolean mIsProfileReady; 51 52 private final CachedBluetoothDeviceManager mDeviceManager; 53 54 static final String NAME = "CSIP Set Coordinator"; 55 private final LocalBluetoothProfileManager mProfileManager; 56 57 // Order of this profile in device profiles list 58 private static final int ORDINAL = 1; 59 60 // These callbacks run on the main thread. 61 private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener { 62 @RequiresApi(33) onServiceConnected(int profile, BluetoothProfile proxy)63 public void onServiceConnected(int profile, BluetoothProfile proxy) { 64 if (VDBG) { 65 Log.d(TAG, "Bluetooth service connected"); 66 } 67 mService = (BluetoothCsipSetCoordinator) proxy; 68 // We just bound to the service, so refresh the UI for any connected CSIP devices. 69 List<BluetoothDevice> deviceList = mService.getConnectedDevices(); 70 while (!deviceList.isEmpty()) { 71 BluetoothDevice nextDevice = deviceList.remove(0); 72 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); 73 // we may add a new device here, but generally this should not happen 74 if (device == null) { 75 if (VDBG) { 76 Log.d(TAG, "CsipSetCoordinatorProfile found new device: " + nextDevice); 77 } 78 device = mDeviceManager.addDevice(nextDevice); 79 } 80 device.onProfileStateChanged( 81 CsipSetCoordinatorProfile.this, BluetoothProfile.STATE_CONNECTED); 82 device.refresh(); 83 } 84 85 mDeviceManager.updateCsipDevices(); 86 mProfileManager.callServiceConnectedListeners(); 87 mIsProfileReady = true; 88 } 89 onServiceDisconnected(int profile)90 public void onServiceDisconnected(int profile) { 91 if (VDBG) { 92 Log.d(TAG, "Bluetooth service disconnected"); 93 } 94 mProfileManager.callServiceDisconnectedListeners(); 95 mIsProfileReady = false; 96 } 97 } 98 CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager)99 CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager, 100 LocalBluetoothProfileManager profileManager) { 101 mContext = context; 102 mDeviceManager = deviceManager; 103 mProfileManager = profileManager; 104 105 BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, 106 new CoordinatedSetServiceListener(), BluetoothProfile.CSIP_SET_COORDINATOR); 107 } 108 109 /** 110 * Get CSIP devices matching connection states{ 111 * 112 * @code BluetoothProfile.STATE_CONNECTED, 113 * @code BluetoothProfile.STATE_CONNECTING, 114 * @code BluetoothProfile.STATE_DISCONNECTING} 115 * 116 * @return Matching device list 117 */ getConnectedDevices()118 public List<BluetoothDevice> getConnectedDevices() { 119 if (mService == null) { 120 return new ArrayList<BluetoothDevice>(0); 121 } 122 return mService.getDevicesMatchingConnectionStates( 123 new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, 124 BluetoothProfile.STATE_DISCONNECTING}); 125 } 126 127 /** 128 * Gets the connection status of the device. 129 * 130 * @code BluetoothProfile.STATE_CONNECTED, 131 * @code BluetoothProfile.STATE_CONNECTING, 132 * @code BluetoothProfile.STATE_DISCONNECTING} 133 * 134 * @return Connection status, {@code BluetoothProfile.STATE_DISCONNECTED} if unknown. 135 */ getConnectionStatus(BluetoothDevice device)136 public int getConnectionStatus(BluetoothDevice device) { 137 if (mService == null) { 138 return BluetoothProfile.STATE_DISCONNECTED; 139 } 140 return mService.getConnectionState(device); 141 } 142 143 @Override isProfileReady()144 public boolean isProfileReady() { 145 return mIsProfileReady; 146 } 147 148 @Override getProfileId()149 public int getProfileId() { 150 return BluetoothProfile.CSIP_SET_COORDINATOR; 151 } 152 153 @Override accessProfileEnabled()154 public boolean accessProfileEnabled() { 155 return false; 156 } 157 158 @Override isAutoConnectable()159 public boolean isAutoConnectable() { 160 return true; 161 } 162 163 @Override isEnabled(BluetoothDevice device)164 public boolean isEnabled(BluetoothDevice device) { 165 if (mService == null || device == null) { 166 return false; 167 } 168 return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; 169 } 170 171 @Override getConnectionPolicy(BluetoothDevice device)172 public int getConnectionPolicy(BluetoothDevice device) { 173 if (mService == null || device == null) { 174 return CONNECTION_POLICY_FORBIDDEN; 175 } 176 return mService.getConnectionPolicy(device); 177 } 178 179 @Override setEnabled(BluetoothDevice device, boolean enabled)180 public boolean setEnabled(BluetoothDevice device, boolean enabled) { 181 boolean isSuccessful = false; 182 if (mService == null || device == null) { 183 return false; 184 } 185 if (enabled) { 186 if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { 187 isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); 188 } 189 } else { 190 isSuccessful = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); 191 } 192 193 return isSuccessful; 194 } 195 196 @Override getOrdinal()197 public int getOrdinal() { 198 return ORDINAL; 199 } 200 201 @Override getNameResource(BluetoothDevice device)202 public int getNameResource(BluetoothDevice device) { 203 return R.string.summary_empty; 204 } 205 206 @Override getSummaryResourceForDevice(BluetoothDevice device)207 public int getSummaryResourceForDevice(BluetoothDevice device) { 208 int state = getConnectionStatus(device); 209 return BluetoothUtils.getConnectionStateSummary(state); 210 } 211 212 @Override getDrawableResource(BluetoothClass btClass)213 public int getDrawableResource(BluetoothClass btClass) { 214 return 0; 215 } 216 217 /** 218 * Get the device's groups and correspondsing uuids map. 219 * @param device the bluetooth device 220 * @return Map of groups ids and related UUIDs 221 */ getGroupUuidMapByDevice(BluetoothDevice device)222 public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(BluetoothDevice device) { 223 if (mService == null || device == null) { 224 return null; 225 } 226 return mService.getGroupUuidMapByDevice(device); 227 } 228 229 /** 230 * Return the profile name as a string. 231 */ toString()232 public String toString() { 233 return NAME; 234 } 235 236 @RequiresApi(33) finalize()237 protected void finalize() { 238 if (VDBG) { 239 Log.d(TAG, "finalize()"); 240 } 241 if (mService != null) { 242 try { 243 BluetoothAdapter.getDefaultAdapter().closeProfileProxy( 244 BluetoothProfile.CSIP_SET_COORDINATOR, mService); 245 mService = null; 246 } catch (Throwable t) { 247 Log.w(TAG, "Error cleaning up CSIP Set Coordinator proxy", t); 248 } 249 } 250 } 251 } 252