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.bluetooth.tbs; 19 20 import android.bluetooth.BluetoothLeCall; 21 import android.bluetooth.IBluetoothLeCallControl; 22 import android.bluetooth.IBluetoothLeCallControlCallback; 23 import android.content.AttributionSource; 24 import android.os.ParcelUuid; 25 import android.os.RemoteException; 26 import android.sysprop.BluetoothProperties; 27 import android.util.Log; 28 29 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 30 31 import com.android.bluetooth.Utils; 32 import com.android.bluetooth.btservice.ProfileService; 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.List; 36 import java.util.UUID; 37 38 public class TbsService extends ProfileService { 39 40 private static final String TAG = "TbsService"; 41 private static final boolean DBG = true; 42 43 private static TbsService sTbsService; 44 45 private final TbsGeneric mTbsGeneric = new TbsGeneric(); 46 isEnabled()47 public static boolean isEnabled() { 48 return BluetoothProperties.isProfileCcpServerEnabled().orElse(false); 49 } 50 51 @Override initBinder()52 protected IProfileServiceBinder initBinder() { 53 return new TbsServerBinder(this); 54 } 55 56 @Override create()57 protected void create() { 58 if (DBG) { 59 Log.d(TAG, "create()"); 60 } 61 } 62 63 @Override start()64 protected boolean start() { 65 66 if (DBG) { 67 Log.d(TAG, "start()"); 68 } 69 if (sTbsService != null) { 70 throw new IllegalStateException("start() called twice"); 71 } 72 73 // Mark service as started 74 setTbsService(this); 75 76 mTbsGeneric.init(new TbsGatt(this)); 77 78 return true; 79 } 80 81 @Override stop()82 protected boolean stop() { 83 if (DBG) { 84 Log.d(TAG, "stop()"); 85 } 86 if (sTbsService == null) { 87 Log.w(TAG, "stop() called before start()"); 88 return true; 89 } 90 91 // Mark service as stopped 92 setTbsService(null); 93 94 if (mTbsGeneric != null) { 95 mTbsGeneric.cleanup(); 96 } 97 98 return true; 99 } 100 101 @Override cleanup()102 protected void cleanup() { 103 if (DBG) { 104 Log.d(TAG, "cleanup()"); 105 } 106 } 107 108 /** 109 * Get the TbsService instance 110 * 111 * @return TbsService instance 112 */ getTbsService()113 public static synchronized TbsService getTbsService() { 114 if (sTbsService == null) { 115 Log.w(TAG, "getTbsService: service is NULL"); 116 return null; 117 } 118 119 if (!sTbsService.isAvailable()) { 120 Log.w(TAG, "getTbsService: service is not available"); 121 return null; 122 } 123 124 return sTbsService; 125 } 126 setTbsService(TbsService instance)127 private static synchronized void setTbsService(TbsService instance) { 128 if (DBG) { 129 Log.d(TAG, "setTbsService: set to=" + instance); 130 } 131 132 sTbsService = instance; 133 } 134 135 /** Binder object: must be a static class or memory leak may occur */ 136 @VisibleForTesting 137 static class TbsServerBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder { 138 private TbsService mService; 139 getService(AttributionSource source)140 private TbsService getService(AttributionSource source) { 141 if (!Utils.checkServiceAvailable(mService, TAG) 142 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 143 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 144 Log.w(TAG, "TbsService call not allowed for non-active user"); 145 return null; 146 } 147 148 if (mService != null) { 149 if (DBG) { 150 Log.d(TAG, "Service available"); 151 } 152 153 enforceBluetoothPrivilegedPermission(mService); 154 return mService; 155 } 156 157 return null; 158 } 159 TbsServerBinder(TbsService service)160 TbsServerBinder(TbsService service) { 161 mService = service; 162 } 163 164 @Override cleanup()165 public void cleanup() { 166 mService = null; 167 } 168 169 @Override registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology, AttributionSource source)170 public void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, 171 List<String> uriSchemes, int capabilities, String providerName, int technology, 172 AttributionSource source) { 173 TbsService service = getService(source); 174 if (service != null) { 175 service.registerBearer(token, callback, uci, uriSchemes, capabilities, providerName, 176 technology); 177 } else { 178 Log.w(TAG, "Service not active"); 179 } 180 } 181 182 @Override unregisterBearer(String token, AttributionSource source)183 public void unregisterBearer(String token, 184 AttributionSource source) { 185 TbsService service = getService(source); 186 if (service != null) { 187 service.unregisterBearer(token); 188 } else { 189 Log.w(TAG, "Service not active"); 190 } 191 } 192 193 @Override requestResult(int ccid, int requestId, int result, AttributionSource source)194 public void requestResult(int ccid, int requestId, int result, 195 AttributionSource source) { 196 TbsService service = getService(source); 197 if (service != null) { 198 service.requestResult(ccid, requestId, result); 199 } else { 200 Log.w(TAG, "Service not active"); 201 } 202 } 203 204 @Override callAdded(int ccid, BluetoothLeCall call, AttributionSource source)205 public void callAdded(int ccid, BluetoothLeCall call, 206 AttributionSource source) { 207 TbsService service = getService(source); 208 if (service != null) { 209 service.callAdded(ccid, call); 210 } else { 211 Log.w(TAG, "Service not active"); 212 } 213 } 214 215 @Override callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source)216 public void callRemoved(int ccid, ParcelUuid callId, int reason, 217 AttributionSource source) { 218 TbsService service = getService(source); 219 if (service != null) { 220 service.callRemoved(ccid, callId.getUuid(), reason); 221 } else { 222 Log.w(TAG, "Service not active"); 223 } 224 } 225 226 @Override callStateChanged(int ccid, ParcelUuid callId, int state, AttributionSource source)227 public void callStateChanged(int ccid, ParcelUuid callId, int state, 228 AttributionSource source) { 229 TbsService service = getService(source); 230 if (service != null) { 231 service.callStateChanged(ccid, callId.getUuid(), state); 232 } else { 233 Log.w(TAG, "Service not active"); 234 } 235 } 236 237 @Override currentCallsList(int ccid, List<BluetoothLeCall> calls, AttributionSource source)238 public void currentCallsList(int ccid, List<BluetoothLeCall> calls, 239 AttributionSource source) { 240 TbsService service = getService(source); 241 if (service != null) { 242 service.currentCallsList(ccid, calls); 243 } else { 244 Log.w(TAG, "Service not active"); 245 } 246 } 247 248 @Override networkStateChanged(int ccid, String providerName, int technology, AttributionSource source)249 public void networkStateChanged(int ccid, String providerName, int technology, 250 AttributionSource source) { 251 TbsService service = getService(source); 252 if (service != null) { 253 service.networkStateChanged(ccid, providerName, technology); 254 } else { 255 Log.w(TAG, "Service not active"); 256 } 257 } 258 } 259 260 @VisibleForTesting registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)261 void registerBearer(String token, IBluetoothLeCallControlCallback callback, String uci, 262 List<String> uriSchemes, int capabilities, String providerName, int technology) { 263 if (DBG) { 264 Log.d(TAG, "registerBearer: token=" + token); 265 } 266 267 boolean success = mTbsGeneric.addBearer(token, callback, uci, uriSchemes, capabilities, 268 providerName, technology); 269 if (success) { 270 try { 271 callback.asBinder().linkToDeath(() -> { 272 Log.e(TAG, token + " application died, removing..."); 273 unregisterBearer(token); 274 }, 0); 275 } catch (RemoteException e) { 276 e.printStackTrace(); 277 } 278 } 279 280 if (DBG) { 281 Log.d(TAG, "registerBearer: token=" + token + " success=" + success); 282 } 283 } 284 285 @VisibleForTesting unregisterBearer(String token)286 void unregisterBearer(String token) { 287 if (DBG) { 288 Log.d(TAG, "unregisterBearer: token=" + token); 289 } 290 291 mTbsGeneric.removeBearer(token); 292 } 293 294 @VisibleForTesting requestResult(int ccid, int requestId, int result)295 public void requestResult(int ccid, int requestId, int result) { 296 if (DBG) { 297 Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" 298 + result); 299 } 300 301 mTbsGeneric.requestResult(ccid, requestId, result); 302 } 303 304 @VisibleForTesting callAdded(int ccid, BluetoothLeCall call)305 void callAdded(int ccid, BluetoothLeCall call) { 306 if (DBG) { 307 Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call); 308 } 309 310 mTbsGeneric.callAdded(ccid, call); 311 } 312 313 @VisibleForTesting callRemoved(int ccid, UUID callId, int reason)314 void callRemoved(int ccid, UUID callId, int reason) { 315 if (DBG) { 316 Log.d(TAG, "callRemoved: ccid=" + ccid + " callId=" + callId + " reason=" + reason); 317 } 318 319 mTbsGeneric.callRemoved(ccid, callId, reason); 320 } 321 322 @VisibleForTesting callStateChanged(int ccid, UUID callId, int state)323 void callStateChanged(int ccid, UUID callId, int state) { 324 if (DBG) { 325 Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state); 326 } 327 328 mTbsGeneric.callStateChanged(ccid, callId, state); 329 } 330 331 @VisibleForTesting currentCallsList(int ccid, List<BluetoothLeCall> calls)332 void currentCallsList(int ccid, List<BluetoothLeCall> calls) { 333 if (DBG) { 334 Log.d(TAG, "currentCallsList: ccid=" + ccid + " calls=" + calls); 335 } 336 337 mTbsGeneric.currentCallsList(ccid, calls); 338 } 339 340 @VisibleForTesting networkStateChanged(int ccid, String providerName, int technology)341 void networkStateChanged(int ccid, String providerName, int technology) { 342 if (DBG) { 343 Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName 344 + " technology=" + technology); 345 } 346 347 mTbsGeneric.networkStateChanged(ccid, providerName, technology); 348 } 349 350 @Override dump(StringBuilder sb)351 public void dump(StringBuilder sb) { 352 super.dump(sb); 353 } 354 } 355