• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.BluetoothDevice;
21 import android.bluetooth.BluetoothLeAudio;
22 import android.bluetooth.BluetoothLeCall;
23 import android.bluetooth.BluetoothLeCallControl;
24 import android.bluetooth.IBluetoothLeCallControlCallback;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.media.AudioManager;
30 import android.net.Uri;
31 import android.os.ParcelUuid;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import com.android.bluetooth.btservice.ServiceFactory;
36 import com.android.bluetooth.le_audio.ContentControlIdKeeper;
37 import com.android.bluetooth.le_audio.LeAudioService;
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.LinkedHashSet;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.TreeMap;
48 import java.util.UUID;
49 
50 /** Container class to store TBS instances */
51 public class TbsGeneric {
52 
53     private static final String TAG = "TbsGeneric";
54     private static final boolean DBG = true;
55 
56     private static final String UCI = "GTBS";
57     private static final String DEFAULT_PROVIDER_NAME = "none";
58     /* Use GSM as default technology value. It is used only
59      * when bearer is not registered. It will be updated on the phone call
60      */
61     private static final int DEFAULT_BEARER_TECHNOLOGY =
62             BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
63     private static final String UNKNOWN_FRIENDLY_NAME = "unknown";
64 
65     /** Class representing the pending request sent to the application */
66     private class Request {
67         BluetoothDevice device;
68         List<UUID> callIdList;
69         int requestedOpcode;
70         int callIndex;
71 
Request(BluetoothDevice device, UUID callId, int requestedOpcode, int callIndex)72         public Request(BluetoothDevice device, UUID callId, int requestedOpcode, int callIndex) {
73             this.device = device;
74             this.callIdList = Arrays.asList(callId);
75             this.requestedOpcode = requestedOpcode;
76             this.callIndex = callIndex;
77         }
78 
Request(BluetoothDevice device, List<ParcelUuid> callIds, int requestedOpcode, int callIndex)79         public Request(BluetoothDevice device, List<ParcelUuid> callIds, int requestedOpcode,
80                 int callIndex) {
81             this.device = device;
82             this.callIdList = new ArrayList<>();
83             for (ParcelUuid callId : callIds) {
84                 this.callIdList.add(callId.getUuid());
85             }
86             this.requestedOpcode = requestedOpcode;
87             this.callIndex = callIndex;
88         }
89     }
90 
91     /* Application-registered TBS instance */
92     private class Bearer {
93         final String token;
94         final IBluetoothLeCallControlCallback callback;
95         final String uci;
96         List<String> uriSchemes;
97         final int capabilities;
98         final int ccid;
99         String providerName;
100         int technology;
101         Map<UUID, Integer> callIdIndexMap = new HashMap<>();
102         Map<Integer, Request> requestMap = new HashMap<>();
103 
Bearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology, int ccid)104         public Bearer(String token, IBluetoothLeCallControlCallback callback, String uci,
105                 List<String> uriSchemes, int capabilities, String providerName, int technology,
106                 int ccid) {
107             this.token = token;
108             this.callback = callback;
109             this.uci = uci;
110             this.uriSchemes = uriSchemes;
111             this.capabilities = capabilities;
112             this.providerName = providerName;
113             this.technology = technology;
114             this.ccid = ccid;
115         }
116     }
117 
118     private boolean mIsInitialized = false;
119     private TbsGatt mTbsGatt = null;
120     private List<Bearer> mBearerList = new ArrayList<>();
121     private int mLastIndexAssigned = TbsCall.INDEX_UNASSIGNED;
122     private Map<Integer, TbsCall> mCurrentCallsList = new TreeMap<>();
123     private Bearer mForegroundBearer = null;
124     private int mLastRequestIdAssigned = 0;
125     private List<String> mUriSchemes = new ArrayList<>(Arrays.asList("tel"));
126     private Receiver mReceiver = null;
127     private int mStoredRingerMode = -1;
128     private final ServiceFactory mFactory = new ServiceFactory();
129     private LeAudioService mLeAudioService;
130 
131     private final class Receiver extends BroadcastReceiver {
132         @Override
onReceive(Context context, Intent intent)133         public void onReceive(Context context, Intent intent) {
134             synchronized (TbsGeneric.this) {
135                 if (!mIsInitialized) {
136                     Log.w(TAG, "onReceive called while not initialized.");
137                     return;
138                 }
139 
140                 final String action = intent.getAction();
141                 if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
142                     int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
143 
144                     if (ringerMode < 0 || ringerMode == mStoredRingerMode) return;
145 
146                     mStoredRingerMode = ringerMode;
147 
148                     if (isSilentModeEnabled()) {
149                         mTbsGatt.setSilentModeFlag();
150                     } else {
151                         mTbsGatt.clearSilentModeFlag();
152                     }
153                 }
154             }
155         }
156     };
157 
init(TbsGatt tbsGatt)158     public synchronized boolean init(TbsGatt tbsGatt) {
159         if (DBG) {
160             Log.d(TAG, "init");
161         }
162         mTbsGatt = tbsGatt;
163 
164         int ccid = ContentControlIdKeeper.acquireCcid(new ParcelUuid(TbsGatt.UUID_GTBS),
165                 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL);
166         if (!isCcidValid(ccid)) {
167             Log.e(TAG, " CCID is not valid");
168             cleanup();
169             return false;
170         }
171 
172         if (!mTbsGatt.init(ccid, UCI, mUriSchemes, true, true, DEFAULT_PROVIDER_NAME,
173                 DEFAULT_BEARER_TECHNOLOGY, mTbsGattCallback)) {
174             Log.e(TAG, " TbsGatt init failed");
175             cleanup();
176             return false;
177         }
178 
179         AudioManager audioManager = mTbsGatt.getContext().getSystemService(AudioManager.class);
180         if (audioManager == null) {
181             Log.w(TAG, " AudioManager is not available");
182             cleanup();
183             return false;
184         }
185 
186         // read initial value of ringer mode
187         mStoredRingerMode = audioManager.getRingerMode();
188 
189         if (isSilentModeEnabled()) {
190             mTbsGatt.setSilentModeFlag();
191         } else {
192             mTbsGatt.clearSilentModeFlag();
193         }
194 
195         mReceiver = new Receiver();
196         IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
197         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
198         mTbsGatt.getContext().registerReceiver(mReceiver, filter);
199 
200         mIsInitialized = true;
201         return true;
202     }
203 
cleanup()204     public synchronized void cleanup() {
205         if (DBG) {
206             Log.d(TAG, "cleanup");
207         }
208 
209         if (mTbsGatt != null) {
210             if (mReceiver != null) {
211                 mTbsGatt.getContext().unregisterReceiver(mReceiver);
212             }
213             mTbsGatt.cleanup();
214             mTbsGatt = null;
215         }
216 
217         mIsInitialized = false;
218     }
219 
220     /**
221      * Inform TBS GATT instance about authorization change for device.
222      *
223      * @param device device for which authorization is changed
224      */
onDeviceAuthorizationSet(BluetoothDevice device)225     public void onDeviceAuthorizationSet(BluetoothDevice device) {
226         // Notify TBS GATT service instance in case of pending operations
227         if (mTbsGatt != null) {
228             mTbsGatt.onDeviceAuthorizationSet(device);
229         }
230     }
231 
232     /**
233      * Set inband ringtone for the device.
234      * When set, notification will be sent to given device.
235      *
236      * @param device    device for which inband ringtone has been set
237      */
setInbandRingtoneSupport(BluetoothDevice device)238     public synchronized void setInbandRingtoneSupport(BluetoothDevice device) {
239         if (mTbsGatt == null) {
240             Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null");
241             return;
242         }
243         mTbsGatt.setInbandRingtoneFlag(device);
244     }
245 
246     /**
247      * Clear inband ringtone for the device.
248      * When set, notification will be sent to given device.
249      *
250      * @param device    device for which inband ringtone has been cleared
251      */
clearInbandRingtoneSupport(BluetoothDevice device)252     public synchronized void clearInbandRingtoneSupport(BluetoothDevice device) {
253         if (mTbsGatt == null) {
254             Log.w(TAG, "setInbandRingtoneSupport, mTbsGatt is null");
255             return;
256         }
257         mTbsGatt.clearInbandRingtoneFlag(device);
258     }
259 
isSilentModeEnabled()260     private synchronized boolean isSilentModeEnabled() {
261         return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL;
262     }
263 
getBearerByToken(String token)264     private synchronized Bearer getBearerByToken(String token) {
265         for (Bearer bearer : mBearerList) {
266             if (bearer.token.equals(token)) {
267                 return bearer;
268             }
269         }
270         return null;
271     }
272 
getBearerByCcid(int ccid)273     private synchronized Bearer getBearerByCcid(int ccid) {
274         for (Bearer bearer : mBearerList) {
275             if (bearer.ccid == ccid) {
276                 return bearer;
277             }
278         }
279         return null;
280     }
281 
getBearerSupportingUri(String uri)282     private synchronized Bearer getBearerSupportingUri(String uri) {
283         for (Bearer bearer : mBearerList) {
284             for (String s : bearer.uriSchemes) {
285                 if (uri.startsWith(s + ":")) {
286                     return bearer;
287                 }
288             }
289         }
290         return null;
291     }
292 
getCallIdByIndex(int callIndex)293     private synchronized Map.Entry<UUID, Bearer> getCallIdByIndex(int callIndex) {
294         for (Bearer bearer : mBearerList) {
295             for (Map.Entry<UUID, Integer> callIdToIndex : bearer.callIdIndexMap.entrySet()) {
296                 if (callIndex == callIdToIndex.getValue()) {
297                     return Map.entry(callIdToIndex.getKey(), bearer);
298                 }
299             }
300         }
301         return null;
302     }
303 
addBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)304     public synchronized boolean addBearer(String token, IBluetoothLeCallControlCallback callback,
305             String uci, List<String> uriSchemes, int capabilities, String providerName,
306             int technology) {
307         if (DBG) {
308             Log.d(TAG,
309                     "addBearer: token=" + token + " uci=" + uci + " uriSchemes=" + uriSchemes
310                             + " capabilities=" + capabilities + " providerName=" + providerName
311                             + " technology=" + technology);
312         }
313         if (!mIsInitialized) {
314             Log.w(TAG, "addBearer called while not initialized.");
315             return false;
316         }
317 
318         if (getBearerByToken(token) != null) {
319             Log.w(TAG, "addBearer: token=" + token + " registered already");
320             return false;
321         }
322 
323         // Acquire CCID for TbsObject. The CCID is released on remove()
324         Bearer bearer = new Bearer(token, callback, uci, uriSchemes, capabilities, providerName,
325                 technology, ContentControlIdKeeper.acquireCcid(new ParcelUuid(UUID.randomUUID()),
326                         BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL));
327         if (isCcidValid(bearer.ccid)) {
328             mBearerList.add(bearer);
329 
330             updateUriSchemesSupported();
331             if (mForegroundBearer == null) {
332                 setForegroundBearer(bearer);
333             }
334         } else {
335             Log.e(TAG, "Failed to acquire ccid");
336         }
337 
338         if (callback != null) {
339             try {
340                 Log.d(TAG, "ccid=" + bearer.ccid);
341                 callback.onBearerRegistered(bearer.ccid);
342             } catch (RemoteException e) {
343                 e.printStackTrace();
344             }
345         }
346 
347         return isCcidValid(bearer.ccid);
348     }
349 
removeBearer(String token)350     public synchronized void removeBearer(String token) {
351         if (DBG) {
352             Log.d(TAG, "removeBearer: token=" + token);
353         }
354 
355         if (!mIsInitialized) {
356             Log.w(TAG, "removeBearer called while not initialized.");
357             return;
358         }
359 
360         Bearer bearer = getBearerByToken(token);
361         if (bearer == null) {
362             return;
363         }
364 
365         // Remove the calls associated with this bearer
366         for (Integer callIndex : bearer.callIdIndexMap.values()) {
367             mCurrentCallsList.remove(callIndex);
368         }
369 
370         if (bearer.callIdIndexMap.size() > 0) {
371             notifyCclc();
372         }
373 
374         // Release the ccid acquired
375         ContentControlIdKeeper.releaseCcid(bearer.ccid);
376 
377         mBearerList.remove(bearer);
378 
379         updateUriSchemesSupported();
380         if (mForegroundBearer == bearer) {
381             setForegroundBearer(findNewForegroundBearer());
382         }
383     }
384 
checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall)385     private synchronized void checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall) {
386         // check if there's any pending request related to this call
387         Map.Entry<Integer, Request> requestEntry = null;
388         if (bearer.requestMap.size() > 0) {
389             for (Map.Entry<Integer, Request> entry : bearer.requestMap.entrySet()) {
390                 if (entry.getValue().callIdList.contains(callId)) {
391                     requestEntry = entry;
392                 }
393             }
394         }
395 
396         if (requestEntry == null) {
397             if (DBG) {
398                 Log.d(TAG, "requestEntry is null");
399             }
400             return;
401         }
402 
403         int requestId = requestEntry.getKey();
404         Request request = requestEntry.getValue();
405 
406         int result;
407         if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) {
408             if (mCurrentCallsList.get(request.callIndex) == null) {
409                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
410             } else {
411                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
412             }
413         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) {
414             if (tbsCall.getState() != BluetoothLeCall.STATE_INCOMING) {
415                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
416             } else {
417                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
418             }
419         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) {
420             if (tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_HELD
421                     || tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
422                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
423             } else {
424                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
425             }
426         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE) {
427             if (tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_HELD
428                     && tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
429                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
430             } else {
431                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
432             }
433         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) {
434             if (bearer.callIdIndexMap.get(request.callIdList.get(0)) != null) {
435                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
436             } else {
437                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
438             }
439         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN) {
440             /* While joining calls, those that are not in remotely held state should go to active */
441             if (bearer.callIdIndexMap.get(callId) == null
442                     || (tbsCall.getState() != BluetoothLeCall.STATE_ACTIVE
443                             && tbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD)) {
444                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
445             } else {
446                 /* Check if all of the pending calls transit to required state */
447                 for (UUID pendingCallId : request.callIdList) {
448                     Integer callIndex = bearer.callIdIndexMap.get(pendingCallId);
449                     TbsCall pendingTbsCall = mCurrentCallsList.get(callIndex);
450                     if (pendingTbsCall.getState() != BluetoothLeCall.STATE_ACTIVE
451                             && pendingTbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD) {
452                         /* Still waiting for more call state updates */
453                         return;
454                     }
455                 }
456                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
457             }
458         } else {
459             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
460 
461         }
462 
463         mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode,
464                 request.callIndex, result);
465 
466         bearer.requestMap.remove(requestId);
467     }
468 
getTbsResult(int result, int requestedOpcode)469     private synchronized int getTbsResult(int result, int requestedOpcode) {
470         if (result == BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID) {
471             return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
472         }
473 
474         if (result == BluetoothLeCallControl.RESULT_ERROR_INVALID_URI
475                 && requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) {
476             return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI;
477         }
478 
479         return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
480     }
481 
requestResult(int ccid, int requestId, int result)482     public synchronized void requestResult(int ccid, int requestId, int result) {
483         if (DBG) {
484             Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result="
485                     + result);
486         }
487 
488         if (!mIsInitialized) {
489             Log.w(TAG, "requestResult called while not initialized.");
490             return;
491         }
492 
493         Bearer bearer = getBearerByCcid(ccid);
494         if (bearer == null) {
495             Log.i(TAG, " Bearer for ccid " + ccid + " does not exist");
496             return;
497         }
498 
499         if (result == BluetoothLeCallControl.RESULT_SUCCESS) {
500             // don't send the success here, wait for state transition instead
501             return;
502         }
503 
504         // check if there's any pending request related to this call
505         Request request = bearer.requestMap.remove(requestId);
506         if (request == null) {
507             // already sent response
508             return;
509         }
510 
511         int tbsResult = getTbsResult(result, request.requestedOpcode);
512         mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode,
513                 request.callIndex, tbsResult);
514     }
515 
callAdded(int ccid, BluetoothLeCall call)516     public synchronized void callAdded(int ccid, BluetoothLeCall call) {
517         if (DBG) {
518             Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call);
519         }
520 
521         if (!mIsInitialized) {
522             Log.w(TAG, "callAdded called while not initialized.");
523             return;
524         }
525 
526         Bearer bearer = getBearerByCcid(ccid);
527         if (bearer == null) {
528             Log.e(TAG, "callAdded: unknown ccid=" + ccid);
529             return;
530         }
531 
532         UUID callId = call.getUuid();
533         if (bearer.callIdIndexMap.containsKey(callId)) {
534             Log.e(TAG, "callAdded: uuidId=" + callId + " already on list");
535             return;
536         }
537 
538         Integer callIndex = getFreeCallIndex();
539         if (callIndex == null) {
540             Log.e(TAG, "callAdded: out of call indices!");
541             return;
542         }
543 
544         bearer.callIdIndexMap.put(callId, callIndex);
545         TbsCall tbsCall = TbsCall.create(call);
546         mCurrentCallsList.put(callIndex, tbsCall);
547 
548         checkRequestComplete(bearer, callId, tbsCall);
549         if (tbsCall.isIncoming()) {
550             mTbsGatt.setIncomingCall(callIndex, tbsCall.getUri());
551         }
552 
553         String friendlyName = tbsCall.getFriendlyName();
554         if (friendlyName == null) {
555             friendlyName = UNKNOWN_FRIENDLY_NAME;
556         }
557         mTbsGatt.setCallFriendlyName(callIndex, friendlyName);
558 
559         notifyCclc();
560         if (mForegroundBearer != bearer) {
561             setForegroundBearer(bearer);
562         }
563     }
564 
callRemoved(int ccid, UUID callId, int reason)565     public synchronized void callRemoved(int ccid, UUID callId, int reason) {
566         if (DBG) {
567             Log.d(TAG, "callRemoved: ccid=" + ccid + "reason=" + reason);
568         }
569 
570         if (!mIsInitialized) {
571             Log.w(TAG, "callRemoved called while not initialized.");
572             return;
573         }
574 
575         Bearer bearer = getBearerByCcid(ccid);
576         if (bearer == null) {
577             Log.e(TAG, "callRemoved: unknown ccid=" + ccid);
578             return;
579         }
580 
581         Integer callIndex = bearer.callIdIndexMap.remove(callId);
582         if (callIndex == null) {
583             Log.e(TAG, "callIndex: is null for callId" + callId);
584             return;
585         }
586 
587         TbsCall tbsCall = mCurrentCallsList.remove(callIndex);
588         if (tbsCall == null) {
589             Log.e(TAG, "callRemoved: no such call");
590             return;
591         }
592 
593         checkRequestComplete(bearer, callId, tbsCall);
594         mTbsGatt.setTerminationReason(callIndex, reason);
595         notifyCclc();
596 
597         Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex();
598         if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) {
599             mTbsGatt.clearIncomingCall();
600             // TODO: check if there's any incoming call more???
601         }
602 
603         Integer friendlyNameCallIndex = mTbsGatt.getCallFriendlyNameIndex();
604         if (friendlyNameCallIndex != null && friendlyNameCallIndex.equals(callIndex)) {
605             mTbsGatt.clearFriendlyName();
606             // TODO: check if there's any incoming/outgoing call more???
607         }
608     }
609 
callStateChanged(int ccid, UUID callId, int state)610     public synchronized void callStateChanged(int ccid, UUID callId, int state) {
611         if (DBG) {
612             Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state);
613         }
614 
615         if (!mIsInitialized) {
616             Log.w(TAG, "callStateChanged called while not initialized.");
617             return;
618         }
619 
620         Bearer bearer = getBearerByCcid(ccid);
621         if (bearer == null) {
622             Log.e(TAG, "callStateChanged: unknown ccid=" + ccid);
623             return;
624         }
625 
626         Integer callIndex = bearer.callIdIndexMap.get(callId);
627         if (callIndex == null) {
628             Log.e(TAG, "callStateChanged: unknown callId=" + callId);
629             return;
630         }
631 
632         TbsCall tbsCall = mCurrentCallsList.get(callIndex);
633         if (tbsCall.getState() == state) {
634             return;
635         }
636 
637         tbsCall.setState(state);
638 
639         checkRequestComplete(bearer, callId, tbsCall);
640         notifyCclc();
641 
642         Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex();
643         if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) {
644             mTbsGatt.clearIncomingCall();
645             // TODO: check if there's any incoming call more???
646         }
647     }
648 
currentCallsList(int ccid, List<BluetoothLeCall> calls)649     public synchronized void currentCallsList(int ccid, List<BluetoothLeCall> calls) {
650         if (DBG) {
651             Log.d(TAG, "currentCallsList: ccid=" + ccid + " callsNum=" + calls.size());
652         }
653 
654         if (!mIsInitialized) {
655             Log.w(TAG, "currentCallsList called while not initialized.");
656             return;
657         }
658 
659         Bearer bearer = getBearerByCcid(ccid);
660         if (bearer == null) {
661             Log.e(TAG, "currentCallsList: unknown ccid=" + ccid);
662             return;
663         }
664 
665         boolean cclc = false;
666         Map<UUID, Integer> storedCallIdList = new HashMap<>(bearer.callIdIndexMap);
667         bearer.callIdIndexMap = new HashMap<>();
668         for (BluetoothLeCall call : calls) {
669             UUID callId = call.getUuid();
670             Integer callIndex = storedCallIdList.get(callId);
671             if (callIndex == null) {
672                 // new call
673                 callIndex = getFreeCallIndex();
674                 if (callIndex == null) {
675                     Log.e(TAG, "currentCallsList: out of call indices!");
676                     continue;
677                 }
678 
679                 mCurrentCallsList.put(callIndex, TbsCall.create(call));
680                 cclc |= true;
681             } else {
682                 TbsCall tbsCall = mCurrentCallsList.get(callIndex);
683                 TbsCall tbsCallNew = TbsCall.create(call);
684                 if (tbsCall != tbsCallNew) {
685                     mCurrentCallsList.replace(callIndex, tbsCallNew);
686                     cclc |= true;
687                 }
688             }
689 
690             bearer.callIdIndexMap.put(callId, callIndex);
691         }
692 
693         for (Map.Entry<UUID, Integer> callIdToIndex : storedCallIdList.entrySet()) {
694             if (!bearer.callIdIndexMap.containsKey(callIdToIndex.getKey())) {
695                 mCurrentCallsList.remove(callIdToIndex.getValue());
696                 cclc |= true;
697             }
698         }
699 
700         if (cclc) {
701             notifyCclc();
702         }
703     }
704 
networkStateChanged(int ccid, String providerName, int technology)705     public synchronized void networkStateChanged(int ccid, String providerName, int technology) {
706         if (DBG) {
707             Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName
708                     + " technology=" + technology);
709         }
710 
711         if (!mIsInitialized) {
712             Log.w(TAG, "networkStateChanged called while not initialized.");
713             return;
714         }
715 
716         Bearer bearer = getBearerByCcid(ccid);
717         if (bearer == null) {
718             return;
719         }
720 
721         boolean providerChanged = !bearer.providerName.equals(providerName);
722         if (providerChanged) {
723             bearer.providerName = providerName;
724         }
725 
726         boolean technologyChanged = bearer.technology != technology;
727         if (technologyChanged) {
728             bearer.technology = technology;
729         }
730 
731         if (bearer == mForegroundBearer) {
732             if (providerChanged) {
733                 mTbsGatt.setBearerProviderName(bearer.providerName);
734             }
735 
736             if (technologyChanged) {
737                 mTbsGatt.setBearerTechnology(bearer.technology);
738             }
739         }
740     }
741 
processOriginateCall(BluetoothDevice device, String uri)742     private synchronized int processOriginateCall(BluetoothDevice device, String uri) {
743         if (uri.startsWith("tel")) {
744             /*
745              * FIXME: For now, process telephone call originate request here, as
746              * BluetoothInCallService might be not running. The BluetoothInCallService is active
747              * when there is a call only.
748              */
749             Log.i(TAG, "originate uri=" + uri);
750             Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri));
751             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
752             mTbsGatt.getContext().startActivity(intent);
753             mTbsGatt.setCallControlPointResult(device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE,
754                     TbsCall.INDEX_UNASSIGNED, TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS);
755         } else {
756             UUID callId = UUID.randomUUID();
757             int requestId = mLastRequestIdAssigned + 1;
758             Request request = new Request(device, callId,
759                     TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, TbsCall.INDEX_UNASSIGNED);
760 
761             Bearer bearer = getBearerSupportingUri(uri);
762             if (bearer == null) {
763                 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI;
764             }
765 
766             try {
767                 bearer.callback.onPlaceCall(requestId, new ParcelUuid(callId), uri);
768             } catch (RemoteException e) {
769                 e.printStackTrace();
770                 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
771             }
772 
773             bearer.requestMap.put(requestId, request);
774             mLastIndexAssigned = requestId;
775         }
776 
777         setActiveLeDevice(device);
778         return TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
779     }
780 
781     private final TbsGatt.Callback mTbsGattCallback = new TbsGatt.Callback() {
782 
783         @Override
784         public void onServiceAdded(boolean success) {
785             synchronized (TbsGeneric.this) {
786                 if (DBG) {
787                     Log.d(TAG, "onServiceAdded: success=" + success);
788                 }
789             }
790         }
791 
792         @Override
793         public boolean isInbandRingtoneEnabled(BluetoothDevice device) {
794             if (!isLeAudioServiceAvailable()) {
795                 Log.i(TAG, "LeAudio service not available");
796                 return false;
797             }
798             int groupId = mLeAudioService.getGroupId(device);
799             return mLeAudioService.isInbandRingtoneEnabled(groupId);
800         }
801 
802         @Override
803         public void onCallControlPointRequest(BluetoothDevice device, int opcode, byte[] args) {
804             synchronized (TbsGeneric.this) {
805                 if (DBG) {
806                     Log.d(TAG, "onCallControlPointRequest: device=" + device + " opcode="
807                             + opcode + " argsLen=" + args.length);
808                 }
809 
810                 if (!mIsInitialized) {
811                     Log.w(TAG, "onCallControlPointRequest called while not initialized.");
812                     return;
813                 }
814 
815                 int result;
816 
817                 switch (opcode) {
818                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT:
819                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE:
820                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD:
821                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: {
822                         if (args.length == 0) {
823                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
824                             break;
825                         }
826 
827                         int callIndex = args[0];
828                         Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex);
829                         if (entry == null) {
830                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
831                             break;
832                         }
833 
834                         TbsCall call = mCurrentCallsList.get(callIndex);
835                         if (!isCallStateTransitionValid(call.getState(), opcode)) {
836                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_STATE_MISMATCH;
837                             break;
838                         }
839 
840                         Bearer bearer = entry.getValue();
841                         UUID callId = entry.getKey();
842                         int requestId = mLastRequestIdAssigned + 1;
843                         Request request = new Request(device, callId, opcode, callIndex);
844                         try {
845                             if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) {
846                                 setActiveLeDevice(device);
847                                 bearer.callback.onAcceptCall(requestId, new ParcelUuid(callId));
848                             } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) {
849                                 bearer.callback.onTerminateCall(requestId, new ParcelUuid(callId));
850                             } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) {
851                                 if ((bearer.capabilities
852                                         & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) {
853                                     result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
854                                     break;
855                                 }
856                                 bearer.callback.onHoldCall(requestId, new ParcelUuid(callId));
857                             } else {
858                                 if ((bearer.capabilities
859                                         & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) {
860                                     result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
861                                     break;
862                                 }
863                                 bearer.callback.onUnholdCall(requestId, new ParcelUuid(callId));
864                             }
865                         } catch (RemoteException e) {
866                             e.printStackTrace();
867                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
868                             break;
869                         }
870 
871                         bearer.requestMap.put(requestId, request);
872                         mLastRequestIdAssigned = requestId;
873 
874                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
875                         break;
876                     }
877 
878                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE: {
879                         result = processOriginateCall(device, new String(args));
880                         break;
881                     }
882 
883                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN: {
884                         // at least 2 call indices are required
885                         if (args.length < 2) {
886                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
887                             break;
888                         }
889 
890                         Map.Entry<UUID, Bearer> firstEntry = null;
891                         List<ParcelUuid> parcelUuids = new ArrayList<>();
892                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
893                         for (int callIndex : args) {
894                             Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex);
895                             if (entry == null) {
896                                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
897                                 break;
898                             }
899 
900                             // state transition is valid, because a call in any state
901                             // can requested to join
902 
903                             if (firstEntry == null) {
904                                 firstEntry = entry;
905                             }
906 
907                             if (firstEntry.getValue() != entry.getValue()) {
908                                 Log.w(TAG, "Cannot join calls from different bearers!");
909                                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
910                                 break;
911                             }
912 
913                             parcelUuids.add(new ParcelUuid(entry.getKey()));
914                         }
915 
916                         if (result != TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) {
917                             break;
918                         }
919 
920                         Bearer bearer = firstEntry.getValue();
921                         Request request = new Request(device, parcelUuids, opcode, args[0]);
922                         int requestId = mLastRequestIdAssigned + 1;
923                         try {
924                             bearer.callback.onJoinCalls(requestId, parcelUuids);
925                         } catch (RemoteException e) {
926                             e.printStackTrace();
927                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
928                             break;
929                         }
930 
931                         bearer.requestMap.put(requestId, request);
932                         mLastIndexAssigned = requestId;
933 
934                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
935                         break;
936                     }
937 
938                     default:
939                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
940                         break;
941                 }
942 
943                 if (result == TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) {
944                     // return here and wait for the request completition from application
945                     return;
946                 }
947 
948                 mTbsGatt.setCallControlPointResult(device, opcode, 0, result);
949             }
950         }
951     };
952 
isCcidValid(int ccid)953     private static boolean isCcidValid(int ccid) {
954         return ccid != ContentControlIdKeeper.CCID_INVALID;
955     }
956 
isCallIndexAssigned(int callIndex)957     private static boolean isCallIndexAssigned(int callIndex) {
958         return callIndex != TbsCall.INDEX_UNASSIGNED;
959     }
960 
getFreeCallIndex()961     private synchronized Integer getFreeCallIndex() {
962         int callIndex = mLastIndexAssigned;
963         for (int i = TbsCall.INDEX_MIN; i <= TbsCall.INDEX_MAX; i++) {
964             callIndex = (callIndex + 1) % TbsCall.INDEX_MAX;
965             if (!isCallIndexAssigned(callIndex)) {
966                 continue;
967             }
968 
969             if (mCurrentCallsList.keySet().contains(callIndex)) {
970                 continue;
971             }
972 
973             mLastIndexAssigned = callIndex;
974 
975             return callIndex;
976         }
977 
978         return null;
979     }
980 
getCallByStates( LinkedHashSet<Integer> states)981     private synchronized Map.Entry<Integer, TbsCall> getCallByStates(
982             LinkedHashSet<Integer> states) {
983         for (Map.Entry<Integer, TbsCall> entry : mCurrentCallsList.entrySet()) {
984             if (states.contains(entry.getValue().getState())) {
985                 return entry;
986             }
987         }
988 
989         return null;
990     }
991 
getForegroundCall()992     private synchronized Map.Entry<Integer, TbsCall> getForegroundCall() {
993         LinkedHashSet<Integer> states = new LinkedHashSet<Integer>();
994         Map.Entry<Integer, TbsCall> foregroundCall;
995 
996         if (mCurrentCallsList.size() == 0) {
997             return null;
998         }
999 
1000         states.add(BluetoothLeCall.STATE_INCOMING);
1001         foregroundCall = getCallByStates(states);
1002         if (foregroundCall != null) {
1003             return foregroundCall;
1004         }
1005 
1006         states.clear();
1007         states.add(BluetoothLeCall.STATE_DIALING);
1008         states.add(BluetoothLeCall.STATE_ALERTING);
1009         foregroundCall = getCallByStates(states);
1010         if (foregroundCall != null) {
1011             return foregroundCall;
1012         }
1013 
1014         states.clear();
1015         states.add(BluetoothLeCall.STATE_ACTIVE);
1016         foregroundCall = getCallByStates(states);
1017         if (foregroundCall != null) {
1018             return foregroundCall;
1019         }
1020 
1021         return null;
1022     }
1023 
findNewForegroundBearer()1024     private synchronized Bearer findNewForegroundBearer() {
1025         if (mBearerList.size() == 0) {
1026             return null;
1027         }
1028 
1029         // the bearer that owns the foreground call
1030         Map.Entry<Integer, TbsCall> foregroundCall = getForegroundCall();
1031         if (foregroundCall != null) {
1032             for (Bearer bearer : mBearerList) {
1033                 if (bearer.callIdIndexMap.values().contains(foregroundCall.getKey())) {
1034                     return bearer;
1035                 }
1036             }
1037         }
1038 
1039         // the last bearer registered
1040         return mBearerList.get(mBearerList.size() - 1);
1041     }
1042 
setForegroundBearer(Bearer bearer)1043     private synchronized void setForegroundBearer(Bearer bearer) {
1044         if (DBG) {
1045             Log.d(TAG, "setForegroundBearer: bearer=" + bearer);
1046         }
1047 
1048         if (bearer == null) {
1049             mTbsGatt.setBearerProviderName(DEFAULT_PROVIDER_NAME);
1050             mTbsGatt.setBearerTechnology(DEFAULT_BEARER_TECHNOLOGY);
1051         } else if (mForegroundBearer == null) {
1052             mTbsGatt.setBearerProviderName(bearer.providerName);
1053             mTbsGatt.setBearerTechnology(bearer.technology);
1054         } else {
1055             if (!bearer.providerName.equals(mForegroundBearer.providerName)) {
1056                 mTbsGatt.setBearerProviderName(bearer.providerName);
1057             }
1058 
1059             if (bearer.technology != mForegroundBearer.technology) {
1060                 mTbsGatt.setBearerTechnology(bearer.technology);
1061             }
1062         }
1063 
1064         mForegroundBearer = bearer;
1065     }
1066 
isLeAudioServiceAvailable()1067     private boolean isLeAudioServiceAvailable() {
1068         if (mLeAudioService != null) {
1069             return true;
1070         }
1071 
1072         mLeAudioService = mFactory.getLeAudioService();
1073         if (mLeAudioService == null) {
1074             Log.e(TAG, "leAudioService not available");
1075             return false;
1076         }
1077 
1078         return true;
1079     }
1080 
1081     @VisibleForTesting
setLeAudioServiceForTesting(LeAudioService leAudioService)1082     void setLeAudioServiceForTesting(LeAudioService leAudioService) {
1083         mLeAudioService = leAudioService;
1084     }
1085 
notifyCclc()1086     private synchronized void notifyCclc() {
1087         if (DBG) {
1088             Log.d(TAG, "notifyCclc");
1089         }
1090 
1091         if (isLeAudioServiceAvailable()) {
1092             if (mCurrentCallsList.size() > 0) {
1093                 mLeAudioService.setInCall(true);
1094             } else {
1095                 mLeAudioService.setInCall(false);
1096             }
1097         }
1098 
1099         mTbsGatt.setCallState(mCurrentCallsList);
1100         mTbsGatt.setBearerListCurrentCalls(mCurrentCallsList);
1101     }
1102 
updateUriSchemesSupported()1103     private synchronized void updateUriSchemesSupported() {
1104         List<String> newUriSchemes = new ArrayList<>();
1105         for (Bearer bearer : mBearerList) {
1106             newUriSchemes.addAll(bearer.uriSchemes);
1107         }
1108 
1109         // filter duplicates
1110         newUriSchemes = new ArrayList<>(new HashSet<>(newUriSchemes));
1111         if (newUriSchemes.equals(mUriSchemes)) {
1112             return;
1113         }
1114 
1115         mUriSchemes = new ArrayList<>(newUriSchemes);
1116         mTbsGatt.setBearerUriSchemesSupportedList(mUriSchemes);
1117     }
1118 
setActiveLeDevice(BluetoothDevice device)1119     private void setActiveLeDevice(BluetoothDevice device) {
1120         if (device == null) {
1121             Log.w(TAG, "setActiveLeDevice: ignore null device");
1122             return;
1123         }
1124         if (!isLeAudioServiceAvailable()) {
1125             Log.w(TAG, "mLeAudioService not available");
1126             return;
1127         }
1128         mLeAudioService.setActiveDevice(device);
1129     }
1130 
isCallStateTransitionValid(int callState, int requestedOpcode)1131     private static boolean isCallStateTransitionValid(int callState, int requestedOpcode) {
1132         switch (requestedOpcode) {
1133             case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT:
1134                 if (callState == BluetoothLeCall.STATE_INCOMING) {
1135                     return true;
1136                 }
1137                 break;
1138 
1139             case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE:
1140                 // Any call can be terminated.
1141                 return true;
1142 
1143             case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD:
1144                 if (callState == BluetoothLeCall.STATE_INCOMING
1145                         || callState == BluetoothLeCall.STATE_ACTIVE
1146                         || callState == BluetoothLeCall.STATE_REMOTELY_HELD) {
1147                     return true;
1148                 }
1149                 break;
1150 
1151             case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE:
1152                 if (callState == BluetoothLeCall.STATE_LOCALLY_HELD
1153                         || callState == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
1154                     return true;
1155                 }
1156                 break;
1157 
1158             default:
1159                 Log.e(TAG, "unhandled opcode " + requestedOpcode);
1160         }
1161 
1162         return false;
1163     }
1164 
1165     /**
1166      * Dump status of TBS service along with related objects
1167      *
1168      * @param sb string builder object that TBS module will be appending
1169      */
dump(StringBuilder sb)1170     public void dump(StringBuilder sb) {
1171         sb.append("\tRinger Mode: " + mStoredRingerMode);
1172 
1173         sb.append("\n\tCurrent call list:");
1174         for (TbsCall call : mCurrentCallsList.values()) {
1175             sb.append("\n\t\tFriendly name: " + call.getSafeFriendlyName());
1176             sb.append("\n\t\t\tState: " + TbsCall.stateToString(call.getState()));
1177             sb.append("\n\t\t\tURI: " +  call.getSafeUri());
1178             sb.append("\n\t\t\tFlags: " + TbsCall.flagsToString(call.getFlags()));
1179         }
1180 
1181         mTbsGatt.dump(sb);
1182     }
1183 }
1184