• 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         // Android supports inband ringtone
196         mTbsGatt.setInbandRingtoneFlag();
197 
198         mReceiver = new Receiver();
199         mTbsGatt.getContext().registerReceiver(mReceiver,
200                 new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION));
201 
202         mIsInitialized = true;
203         return true;
204     }
205 
cleanup()206     public synchronized void cleanup() {
207         if (DBG) {
208             Log.d(TAG, "cleanup");
209         }
210 
211         if (mTbsGatt != null) {
212             if (mReceiver != null) {
213                 mTbsGatt.getContext().unregisterReceiver(mReceiver);
214             }
215             mTbsGatt.cleanup();
216             mTbsGatt = null;
217         }
218 
219         mIsInitialized = false;
220     }
221 
isSilentModeEnabled()222     private synchronized boolean isSilentModeEnabled() {
223         return mStoredRingerMode != AudioManager.RINGER_MODE_NORMAL;
224     }
225 
getBearerByToken(String token)226     private synchronized Bearer getBearerByToken(String token) {
227         for (Bearer bearer : mBearerList) {
228             if (bearer.token.equals(token)) {
229                 return bearer;
230             }
231         }
232         return null;
233     }
234 
getBearerByCcid(int ccid)235     private synchronized Bearer getBearerByCcid(int ccid) {
236         for (Bearer bearer : mBearerList) {
237             if (bearer.ccid == ccid) {
238                 return bearer;
239             }
240         }
241         return null;
242     }
243 
getBearerSupportingUri(String uri)244     private synchronized Bearer getBearerSupportingUri(String uri) {
245         for (Bearer bearer : mBearerList) {
246             for (String s : bearer.uriSchemes) {
247                 if (uri.startsWith(s + ":")) {
248                     return bearer;
249                 }
250             }
251         }
252         return null;
253     }
254 
getCallIdByIndex(int callIndex)255     private synchronized Map.Entry<UUID, Bearer> getCallIdByIndex(int callIndex) {
256         for (Bearer bearer : mBearerList) {
257             for (Map.Entry<UUID, Integer> callIdToIndex : bearer.callIdIndexMap.entrySet()) {
258                 if (callIndex == callIdToIndex.getValue()) {
259                     return Map.entry(callIdToIndex.getKey(), bearer);
260                 }
261             }
262         }
263         return null;
264     }
265 
addBearer(String token, IBluetoothLeCallControlCallback callback, String uci, List<String> uriSchemes, int capabilities, String providerName, int technology)266     public synchronized boolean addBearer(String token, IBluetoothLeCallControlCallback callback,
267             String uci, List<String> uriSchemes, int capabilities, String providerName,
268             int technology) {
269         if (DBG) {
270             Log.d(TAG,
271                     "addBearer: token=" + token + " uci=" + uci + " uriSchemes=" + uriSchemes
272                             + " capabilities=" + capabilities + " providerName=" + providerName
273                             + " technology=" + technology);
274         }
275         if (!mIsInitialized) {
276             Log.w(TAG, "addBearer called while not initialized.");
277             return false;
278         }
279 
280         if (getBearerByToken(token) != null) {
281             Log.w(TAG, "addBearer: token=" + token + " registered already");
282             return false;
283         }
284 
285         // Acquire CCID for TbsObject. The CCID is released on remove()
286         Bearer bearer = new Bearer(token, callback, uci, uriSchemes, capabilities, providerName,
287                 technology, ContentControlIdKeeper.acquireCcid(new ParcelUuid(UUID.randomUUID()),
288                         BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL));
289         if (isCcidValid(bearer.ccid)) {
290             mBearerList.add(bearer);
291 
292             updateUriSchemesSupported();
293             if (mForegroundBearer == null) {
294                 setForegroundBearer(bearer);
295             }
296         } else {
297             Log.e(TAG, "Failed to acquire ccid");
298         }
299 
300         if (callback != null) {
301             try {
302                 Log.d(TAG, "ccid=" + bearer.ccid);
303                 callback.onBearerRegistered(bearer.ccid);
304             } catch (RemoteException e) {
305                 e.printStackTrace();
306             }
307         }
308 
309         return isCcidValid(bearer.ccid);
310     }
311 
removeBearer(String token)312     public synchronized void removeBearer(String token) {
313         if (DBG) {
314             Log.d(TAG, "removeBearer: token=" + token);
315         }
316 
317         if (!mIsInitialized) {
318             Log.w(TAG, "removeBearer called while not initialized.");
319             return;
320         }
321 
322         Bearer bearer = getBearerByToken(token);
323         if (bearer == null) {
324             return;
325         }
326 
327         // Remove the calls associated with this bearer
328         for (Integer callIndex : bearer.callIdIndexMap.values()) {
329             mCurrentCallsList.remove(callIndex);
330         }
331 
332         if (bearer.callIdIndexMap.size() > 0) {
333             notifyCclc();
334         }
335 
336         // Release the ccid acquired
337         ContentControlIdKeeper.releaseCcid(bearer.ccid);
338 
339         mBearerList.remove(bearer);
340 
341         updateUriSchemesSupported();
342         if (mForegroundBearer == bearer) {
343             setForegroundBearer(findNewForegroundBearer());
344         }
345     }
346 
checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall)347     private synchronized void checkRequestComplete(Bearer bearer, UUID callId, TbsCall tbsCall) {
348         // check if there's any pending request related to this call
349         Map.Entry<Integer, Request> requestEntry = null;
350         if (bearer.requestMap.size() > 0) {
351             for (Map.Entry<Integer, Request> entry : bearer.requestMap.entrySet()) {
352                 if (entry.getValue().callIdList.contains(callId)) {
353                     requestEntry = entry;
354                 }
355             }
356         }
357 
358         if (requestEntry == null) {
359             if (DBG) {
360                 Log.d(TAG, "requestEntry is null");
361             }
362             return;
363         }
364 
365         int requestId = requestEntry.getKey();
366         Request request = requestEntry.getValue();
367 
368         int result;
369         if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) {
370             if (mCurrentCallsList.get(request.callIndex) == null) {
371                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
372             } else {
373                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
374             }
375         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) {
376             if (tbsCall.getState() != BluetoothLeCall.STATE_INCOMING) {
377                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
378             } else {
379                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
380             }
381         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) {
382             if (tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_HELD
383                     || tbsCall.getState() == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
384                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
385             } else {
386                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
387             }
388         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE) {
389             if (tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_HELD
390                     && tbsCall.getState() != BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
391                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
392             } else {
393                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
394             }
395         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) {
396             if (bearer.callIdIndexMap.get(request.callIdList.get(0)) != null) {
397                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
398             } else {
399                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
400             }
401         } else if (request.requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN) {
402             /* While joining calls, those that are not in remotely held state should go to active */
403             if (bearer.callIdIndexMap.get(callId) == null
404                     || (tbsCall.getState() != BluetoothLeCall.STATE_ACTIVE
405                             && tbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD)) {
406                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
407             } else {
408                 /* Check if all of the pending calls transit to required state */
409                 for (UUID pendingCallId : request.callIdList) {
410                     Integer callIndex = bearer.callIdIndexMap.get(pendingCallId);
411                     TbsCall pendingTbsCall = mCurrentCallsList.get(callIndex);
412                     if (pendingTbsCall.getState() != BluetoothLeCall.STATE_ACTIVE
413                             && pendingTbsCall.getState() != BluetoothLeCall.STATE_REMOTELY_HELD) {
414                         /* Still waiting for more call state updates */
415                         return;
416                     }
417                 }
418                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
419             }
420         } else {
421             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
422 
423         }
424 
425         mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode,
426                 request.callIndex, result);
427 
428         bearer.requestMap.remove(requestId);
429     }
430 
getTbsResult(int result, int requestedOpcode)431     private synchronized int getTbsResult(int result, int requestedOpcode) {
432         if (result == BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID) {
433             return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
434         }
435 
436         if (result == BluetoothLeCallControl.RESULT_ERROR_INVALID_URI
437                 && requestedOpcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE) {
438             return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI;
439         }
440 
441         return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
442     }
443 
requestResult(int ccid, int requestId, int result)444     public synchronized void requestResult(int ccid, int requestId, int result) {
445         if (DBG) {
446             Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result="
447                     + result);
448         }
449 
450         if (!mIsInitialized) {
451             Log.w(TAG, "requestResult called while not initialized.");
452             return;
453         }
454 
455         Bearer bearer = getBearerByCcid(ccid);
456         if (bearer == null) {
457             Log.i(TAG, " Bearer for ccid " + ccid + " does not exist");
458             return;
459         }
460 
461         if (result == BluetoothLeCallControl.RESULT_SUCCESS) {
462             // don't send the success here, wait for state transition instead
463             return;
464         }
465 
466         // check if there's any pending request related to this call
467         Request request = bearer.requestMap.remove(requestId);
468         if (request == null) {
469             // already sent response
470             return;
471         }
472 
473         int tbsResult = getTbsResult(result, request.requestedOpcode);
474         mTbsGatt.setCallControlPointResult(request.device, request.requestedOpcode,
475                 request.callIndex, tbsResult);
476     }
477 
callAdded(int ccid, BluetoothLeCall call)478     public synchronized void callAdded(int ccid, BluetoothLeCall call) {
479         if (DBG) {
480             Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call);
481         }
482 
483         if (!mIsInitialized) {
484             Log.w(TAG, "callAdded called while not initialized.");
485             return;
486         }
487 
488         Bearer bearer = getBearerByCcid(ccid);
489         if (bearer == null) {
490             Log.e(TAG, "callAdded: unknown ccid=" + ccid);
491             return;
492         }
493 
494         UUID callId = call.getUuid();
495         if (bearer.callIdIndexMap.containsKey(callId)) {
496             Log.e(TAG, "callAdded: uuidId=" + callId + " already on list");
497             return;
498         }
499 
500         Integer callIndex = getFreeCallIndex();
501         if (callIndex == null) {
502             Log.e(TAG, "callAdded: out of call indices!");
503             return;
504         }
505 
506         bearer.callIdIndexMap.put(callId, callIndex);
507         TbsCall tbsCall = TbsCall.create(call);
508         mCurrentCallsList.put(callIndex, tbsCall);
509 
510         checkRequestComplete(bearer, callId, tbsCall);
511         if (tbsCall.isIncoming()) {
512             mTbsGatt.setIncomingCall(callIndex, tbsCall.getUri());
513         }
514 
515         String friendlyName = tbsCall.getFriendlyName();
516         if (friendlyName == null) {
517             friendlyName = UNKNOWN_FRIENDLY_NAME;
518         }
519         mTbsGatt.setCallFriendlyName(callIndex, friendlyName);
520 
521         notifyCclc();
522         if (mForegroundBearer != bearer) {
523             setForegroundBearer(bearer);
524         }
525     }
526 
callRemoved(int ccid, UUID callId, int reason)527     public synchronized void callRemoved(int ccid, UUID callId, int reason) {
528         if (DBG) {
529             Log.d(TAG, "callRemoved: ccid=" + ccid + "reason=" + reason);
530         }
531 
532         if (!mIsInitialized) {
533             Log.w(TAG, "callRemoved called while not initialized.");
534             return;
535         }
536 
537         Bearer bearer = getBearerByCcid(ccid);
538         if (bearer == null) {
539             Log.e(TAG, "callRemoved: unknown ccid=" + ccid);
540             return;
541         }
542 
543         Integer callIndex = bearer.callIdIndexMap.remove(callId);
544         if (callIndex == null) {
545             Log.e(TAG, "callIndex: is null for callId" + callId);
546             return;
547         }
548 
549         TbsCall tbsCall = mCurrentCallsList.remove(callIndex);
550         if (tbsCall == null) {
551             Log.e(TAG, "callRemoved: no such call");
552             return;
553         }
554 
555         checkRequestComplete(bearer, callId, tbsCall);
556         mTbsGatt.setTerminationReason(callIndex, reason);
557         notifyCclc();
558 
559         Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex();
560         if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) {
561             mTbsGatt.clearIncomingCall();
562             // TODO: check if there's any incoming call more???
563         }
564 
565         Integer friendlyNameCallIndex = mTbsGatt.getCallFriendlyNameIndex();
566         if (friendlyNameCallIndex != null && friendlyNameCallIndex.equals(callIndex)) {
567             mTbsGatt.clearFriendlyName();
568             // TODO: check if there's any incoming/outgoing call more???
569         }
570     }
571 
callStateChanged(int ccid, UUID callId, int state)572     public synchronized void callStateChanged(int ccid, UUID callId, int state) {
573         if (DBG) {
574             Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state);
575         }
576 
577         if (!mIsInitialized) {
578             Log.w(TAG, "callStateChanged called while not initialized.");
579             return;
580         }
581 
582         Bearer bearer = getBearerByCcid(ccid);
583         if (bearer == null) {
584             Log.e(TAG, "callStateChanged: unknown ccid=" + ccid);
585             return;
586         }
587 
588         Integer callIndex = bearer.callIdIndexMap.get(callId);
589         if (callIndex == null) {
590             Log.e(TAG, "callStateChanged: unknown callId=" + callId);
591             return;
592         }
593 
594         TbsCall tbsCall = mCurrentCallsList.get(callIndex);
595         if (tbsCall.getState() == state) {
596             return;
597         }
598 
599         tbsCall.setState(state);
600 
601         checkRequestComplete(bearer, callId, tbsCall);
602         notifyCclc();
603 
604         Integer incomingCallIndex = mTbsGatt.getIncomingCallIndex();
605         if (incomingCallIndex != null && incomingCallIndex.equals(callIndex)) {
606             mTbsGatt.clearIncomingCall();
607             // TODO: check if there's any incoming call more???
608         }
609     }
610 
currentCallsList(int ccid, List<BluetoothLeCall> calls)611     public synchronized void currentCallsList(int ccid, List<BluetoothLeCall> calls) {
612         if (DBG) {
613             Log.d(TAG, "currentCallsList: ccid=" + ccid + " callsNum=" + calls.size());
614         }
615 
616         if (!mIsInitialized) {
617             Log.w(TAG, "currentCallsList called while not initialized.");
618             return;
619         }
620 
621         Bearer bearer = getBearerByCcid(ccid);
622         if (bearer == null) {
623             Log.e(TAG, "currentCallsList: unknown ccid=" + ccid);
624             return;
625         }
626 
627         boolean cclc = false;
628         Map<UUID, Integer> storedCallIdList = new HashMap<>(bearer.callIdIndexMap);
629         bearer.callIdIndexMap = new HashMap<>();
630         for (BluetoothLeCall call : calls) {
631             UUID callId = call.getUuid();
632             Integer callIndex = storedCallIdList.get(callId);
633             if (callIndex == null) {
634                 // new call
635                 callIndex = getFreeCallIndex();
636                 if (callIndex == null) {
637                     Log.e(TAG, "currentCallsList: out of call indices!");
638                     continue;
639                 }
640 
641                 mCurrentCallsList.put(callIndex, TbsCall.create(call));
642                 cclc |= true;
643             } else {
644                 TbsCall tbsCall = mCurrentCallsList.get(callIndex);
645                 TbsCall tbsCallNew = TbsCall.create(call);
646                 if (tbsCall != tbsCallNew) {
647                     mCurrentCallsList.replace(callIndex, tbsCallNew);
648                     cclc |= true;
649                 }
650             }
651 
652             bearer.callIdIndexMap.put(callId, callIndex);
653         }
654 
655         for (Map.Entry<UUID, Integer> callIdToIndex : storedCallIdList.entrySet()) {
656             if (!bearer.callIdIndexMap.containsKey(callIdToIndex.getKey())) {
657                 mCurrentCallsList.remove(callIdToIndex.getValue());
658                 cclc |= true;
659             }
660         }
661 
662         if (cclc) {
663             notifyCclc();
664         }
665     }
666 
networkStateChanged(int ccid, String providerName, int technology)667     public synchronized void networkStateChanged(int ccid, String providerName, int technology) {
668         if (DBG) {
669             Log.d(TAG, "networkStateChanged: ccid=" + ccid + " providerName=" + providerName
670                     + " technology=" + technology);
671         }
672 
673         if (!mIsInitialized) {
674             Log.w(TAG, "networkStateChanged called while not initialized.");
675             return;
676         }
677 
678         Bearer bearer = getBearerByCcid(ccid);
679         if (bearer == null) {
680             return;
681         }
682 
683         boolean providerChanged = !bearer.providerName.equals(providerName);
684         if (providerChanged) {
685             bearer.providerName = providerName;
686         }
687 
688         boolean technologyChanged = bearer.technology != technology;
689         if (technologyChanged) {
690             bearer.technology = technology;
691         }
692 
693         if (bearer == mForegroundBearer) {
694             if (providerChanged) {
695                 mTbsGatt.setBearerProviderName(bearer.providerName);
696             }
697 
698             if (technologyChanged) {
699                 mTbsGatt.setBearerTechnology(bearer.technology);
700             }
701         }
702     }
703 
processOriginateCall(BluetoothDevice device, String uri)704     private synchronized int processOriginateCall(BluetoothDevice device, String uri) {
705         if (uri.startsWith("tel")) {
706             /*
707              * FIXME: For now, process telephone call originate request here, as
708              * BluetoothInCallService might be not running. The BluetoothInCallService is active
709              * when there is a call only.
710              */
711             Log.i(TAG, "originate uri=" + uri);
712             Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.parse(uri));
713             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
714             mTbsGatt.getContext().startActivity(intent);
715             mTbsGatt.setCallControlPointResult(device, TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE,
716                     TbsCall.INDEX_UNASSIGNED, TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS);
717         } else {
718             UUID callId = UUID.randomUUID();
719             int requestId = mLastRequestIdAssigned + 1;
720             Request request = new Request(device, callId,
721                     TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE, TbsCall.INDEX_UNASSIGNED);
722 
723             Bearer bearer = getBearerSupportingUri(uri);
724             if (bearer == null) {
725                 return TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_OUTGOING_URI;
726             }
727 
728             try {
729                 bearer.callback.onPlaceCall(requestId, new ParcelUuid(callId), uri);
730             } catch (RemoteException e) {
731                 e.printStackTrace();
732                 return TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
733             }
734 
735             bearer.requestMap.put(requestId, request);
736             mLastIndexAssigned = requestId;
737         }
738 
739         setActiveLeDevice(device);
740         return TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
741     }
742 
743     private final TbsGatt.Callback mTbsGattCallback = new TbsGatt.Callback() {
744 
745         @Override
746         public void onServiceAdded(boolean success) {
747             synchronized (TbsGeneric.this) {
748                 if (DBG) {
749                     Log.d(TAG, "onServiceAdded: success=" + success);
750                 }
751             }
752         }
753 
754         @Override
755         public void onCallControlPointRequest(BluetoothDevice device, int opcode, byte[] args) {
756             synchronized (TbsGeneric.this) {
757                 if (DBG) {
758                     Log.d(TAG, "onCallControlPointRequest: device=" + device + " opcode="
759                             + opcode + " argsLen=" + args.length);
760                 }
761 
762                 if (!mIsInitialized) {
763                     Log.w(TAG, "onCallControlPointRequest called while not initialized.");
764                     return;
765                 }
766 
767                 int result;
768 
769                 switch (opcode) {
770                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT:
771                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE:
772                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD:
773                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE: {
774                         if (args.length == 0) {
775                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
776                             break;
777                         }
778 
779                         int callIndex = args[0];
780                         Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex);
781                         if (entry == null) {
782                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
783                             break;
784                         }
785 
786                         TbsCall call = mCurrentCallsList.get(callIndex);
787                         if (!isCallStateTransitionValid(call.getState(), opcode)) {
788                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_STATE_MISMATCH;
789                             break;
790                         }
791 
792                         Bearer bearer = entry.getValue();
793                         UUID callId = entry.getKey();
794                         int requestId = mLastRequestIdAssigned + 1;
795                         Request request = new Request(device, callId, opcode, callIndex);
796                         try {
797                             if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT) {
798                                 setActiveLeDevice(device);
799                                 bearer.callback.onAcceptCall(requestId, new ParcelUuid(callId));
800                             } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE) {
801                                 bearer.callback.onTerminateCall(requestId, new ParcelUuid(callId));
802                             } else if (opcode == TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD) {
803                                 if ((bearer.capabilities
804                                         & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) {
805                                     result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
806                                     break;
807                                 }
808                                 bearer.callback.onHoldCall(requestId, new ParcelUuid(callId));
809                             } else {
810                                 if ((bearer.capabilities
811                                         & BluetoothLeCallControl.CAPABILITY_HOLD_CALL) == 0) {
812                                     result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
813                                     break;
814                                 }
815                                 bearer.callback.onUnholdCall(requestId, new ParcelUuid(callId));
816                             }
817                         } catch (RemoteException e) {
818                             e.printStackTrace();
819                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
820                             break;
821                         }
822 
823                         bearer.requestMap.put(requestId, request);
824                         mLastRequestIdAssigned = requestId;
825 
826                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
827                         break;
828                     }
829 
830                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_ORIGINATE: {
831                         result = processOriginateCall(device, new String(args));
832                         break;
833                     }
834 
835                     case TbsGatt.CALL_CONTROL_POINT_OPCODE_JOIN: {
836                         // at least 2 call indices are required
837                         if (args.length < 2) {
838                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
839                             break;
840                         }
841 
842                         Map.Entry<UUID, Bearer> firstEntry = null;
843                         List<ParcelUuid> parcelUuids = new ArrayList<>();
844                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
845                         for (int callIndex : args) {
846                             Map.Entry<UUID, Bearer> entry = getCallIdByIndex(callIndex);
847                             if (entry == null) {
848                                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_INVALID_CALL_INDEX;
849                                 break;
850                             }
851 
852                             // state transition is valid, because a call in any state
853                             // can requested to join
854 
855                             if (firstEntry == null) {
856                                 firstEntry = entry;
857                             }
858 
859                             if (firstEntry.getValue() != entry.getValue()) {
860                                 Log.w(TAG, "Cannot join calls from different bearers!");
861                                 result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
862                                 break;
863                             }
864 
865                             parcelUuids.add(new ParcelUuid(entry.getKey()));
866                         }
867 
868                         if (result != TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) {
869                             break;
870                         }
871 
872                         Bearer bearer = firstEntry.getValue();
873                         Request request = new Request(device, parcelUuids, opcode, args[0]);
874                         int requestId = mLastRequestIdAssigned + 1;
875                         try {
876                             bearer.callback.onJoinCalls(requestId, parcelUuids);
877                         } catch (RemoteException e) {
878                             e.printStackTrace();
879                             result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE;
880                             break;
881                         }
882 
883                         bearer.requestMap.put(requestId, request);
884                         mLastIndexAssigned = requestId;
885 
886                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS;
887                         break;
888                     }
889 
890                     default:
891                         result = TbsGatt.CALL_CONTROL_POINT_RESULT_OPCODE_NOT_SUPPORTED;
892                         break;
893                 }
894 
895                 if (result == TbsGatt.CALL_CONTROL_POINT_RESULT_SUCCESS) {
896                     // return here and wait for the request completition from application
897                     return;
898                 }
899 
900                 mTbsGatt.setCallControlPointResult(device, opcode, 0, result);
901             }
902         }
903     };
904 
isCcidValid(int ccid)905     private static boolean isCcidValid(int ccid) {
906         return ccid != ContentControlIdKeeper.CCID_INVALID;
907     }
908 
isCallIndexAssigned(int callIndex)909     private static boolean isCallIndexAssigned(int callIndex) {
910         return callIndex != TbsCall.INDEX_UNASSIGNED;
911     }
912 
getFreeCallIndex()913     private synchronized Integer getFreeCallIndex() {
914         int callIndex = mLastIndexAssigned;
915         for (int i = TbsCall.INDEX_MIN; i <= TbsCall.INDEX_MAX; i++) {
916             callIndex = (callIndex + 1) % TbsCall.INDEX_MAX;
917             if (!isCallIndexAssigned(callIndex)) {
918                 continue;
919             }
920 
921             if (mCurrentCallsList.keySet().contains(callIndex)) {
922                 continue;
923             }
924 
925             mLastIndexAssigned = callIndex;
926 
927             return callIndex;
928         }
929 
930         return null;
931     }
932 
getCallByStates( LinkedHashSet<Integer> states)933     private synchronized Map.Entry<Integer, TbsCall> getCallByStates(
934             LinkedHashSet<Integer> states) {
935         for (Map.Entry<Integer, TbsCall> entry : mCurrentCallsList.entrySet()) {
936             if (states.contains(entry.getValue().getState())) {
937                 return entry;
938             }
939         }
940 
941         return null;
942     }
943 
getForegroundCall()944     private synchronized Map.Entry<Integer, TbsCall> getForegroundCall() {
945         LinkedHashSet<Integer> states = new LinkedHashSet<Integer>();
946         Map.Entry<Integer, TbsCall> foregroundCall;
947 
948         if (mCurrentCallsList.size() == 0) {
949             return null;
950         }
951 
952         states.add(BluetoothLeCall.STATE_INCOMING);
953         foregroundCall = getCallByStates(states);
954         if (foregroundCall != null) {
955             return foregroundCall;
956         }
957 
958         states.clear();
959         states.add(BluetoothLeCall.STATE_DIALING);
960         states.add(BluetoothLeCall.STATE_ALERTING);
961         foregroundCall = getCallByStates(states);
962         if (foregroundCall != null) {
963             return foregroundCall;
964         }
965 
966         states.clear();
967         states.add(BluetoothLeCall.STATE_ACTIVE);
968         foregroundCall = getCallByStates(states);
969         if (foregroundCall != null) {
970             return foregroundCall;
971         }
972 
973         return null;
974     }
975 
findNewForegroundBearer()976     private synchronized Bearer findNewForegroundBearer() {
977         if (mBearerList.size() == 0) {
978             return null;
979         }
980 
981         // the bearer that owns the foreground call
982         Map.Entry<Integer, TbsCall> foregroundCall = getForegroundCall();
983         if (foregroundCall != null) {
984             for (Bearer bearer : mBearerList) {
985                 if (bearer.callIdIndexMap.values().contains(foregroundCall.getKey())) {
986                     return bearer;
987                 }
988             }
989         }
990 
991         // the last bearer registered
992         return mBearerList.get(mBearerList.size() - 1);
993     }
994 
setForegroundBearer(Bearer bearer)995     private synchronized void setForegroundBearer(Bearer bearer) {
996         if (DBG) {
997             Log.d(TAG, "setForegroundBearer: bearer=" + bearer);
998         }
999 
1000         if (bearer == null) {
1001             mTbsGatt.setBearerProviderName(DEFAULT_PROVIDER_NAME);
1002             mTbsGatt.setBearerTechnology(DEFAULT_BEARER_TECHNOLOGY);
1003         } else if (mForegroundBearer == null) {
1004             mTbsGatt.setBearerProviderName(bearer.providerName);
1005             mTbsGatt.setBearerTechnology(bearer.technology);
1006         } else {
1007             if (!bearer.providerName.equals(mForegroundBearer.providerName)) {
1008                 mTbsGatt.setBearerProviderName(bearer.providerName);
1009             }
1010 
1011             if (bearer.technology != mForegroundBearer.technology) {
1012                 mTbsGatt.setBearerTechnology(bearer.technology);
1013             }
1014         }
1015 
1016         mForegroundBearer = bearer;
1017     }
1018 
isLeAudioServiceAvailable()1019     private boolean isLeAudioServiceAvailable() {
1020         if (mLeAudioService != null) {
1021             return true;
1022         }
1023 
1024         mLeAudioService = mFactory.getLeAudioService();
1025         if (mLeAudioService == null) {
1026             Log.e(TAG, "leAudioService not available");
1027             return false;
1028         }
1029 
1030         return true;
1031     }
1032 
1033     @VisibleForTesting
setLeAudioServiceForTesting(LeAudioService leAudioService)1034     void setLeAudioServiceForTesting(LeAudioService leAudioService) {
1035         mLeAudioService = leAudioService;
1036     }
1037 
notifyCclc()1038     private synchronized void notifyCclc() {
1039         if (DBG) {
1040             Log.d(TAG, "notifyCclc");
1041         }
1042 
1043         if (isLeAudioServiceAvailable()) {
1044             if (mCurrentCallsList.size() > 0) {
1045                 mLeAudioService.setInCall(true);
1046             } else {
1047                 mLeAudioService.setInCall(false);
1048             }
1049         }
1050 
1051         mTbsGatt.setCallState(mCurrentCallsList);
1052         mTbsGatt.setBearerListCurrentCalls(mCurrentCallsList);
1053     }
1054 
updateUriSchemesSupported()1055     private synchronized void updateUriSchemesSupported() {
1056         List<String> newUriSchemes = new ArrayList<>();
1057         for (Bearer bearer : mBearerList) {
1058             newUriSchemes.addAll(bearer.uriSchemes);
1059         }
1060 
1061         // filter duplicates
1062         newUriSchemes = new ArrayList<>(new HashSet<>(newUriSchemes));
1063         if (newUriSchemes.equals(mUriSchemes)) {
1064             return;
1065         }
1066 
1067         mUriSchemes = new ArrayList<>(newUriSchemes);
1068         mTbsGatt.setBearerUriSchemesSupportedList(mUriSchemes);
1069     }
1070 
setActiveLeDevice(BluetoothDevice device)1071     private void setActiveLeDevice(BluetoothDevice device) {
1072         if (device == null) {
1073             Log.w(TAG, "setActiveLeDevice: ignore null device");
1074             return;
1075         }
1076         if (!isLeAudioServiceAvailable()) {
1077             Log.w(TAG, "mLeAudioService not available");
1078             return;
1079         }
1080         mLeAudioService.setActiveDevice(device);
1081     }
1082 
isCallStateTransitionValid(int callState, int requestedOpcode)1083     private static boolean isCallStateTransitionValid(int callState, int requestedOpcode) {
1084         switch (requestedOpcode) {
1085             case TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT:
1086                 if (callState == BluetoothLeCall.STATE_INCOMING) {
1087                     return true;
1088                 }
1089                 break;
1090 
1091             case TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE:
1092                 // Any call can be terminated.
1093                 return true;
1094 
1095             case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_HOLD:
1096                 if (callState == BluetoothLeCall.STATE_INCOMING
1097                         || callState == BluetoothLeCall.STATE_ACTIVE
1098                         || callState == BluetoothLeCall.STATE_REMOTELY_HELD) {
1099                     return true;
1100                 }
1101                 break;
1102 
1103             case TbsGatt.CALL_CONTROL_POINT_OPCODE_LOCAL_RETRIEVE:
1104                 if (callState == BluetoothLeCall.STATE_LOCALLY_HELD
1105                         || callState == BluetoothLeCall.STATE_LOCALLY_AND_REMOTELY_HELD) {
1106                     return true;
1107                 }
1108                 break;
1109 
1110             default:
1111                 Log.e(TAG, "unhandled opcode " + requestedOpcode);
1112         }
1113 
1114         return false;
1115     }
1116 }
1117