1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.bass_client;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import android.annotation.Nullable;
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothGatt;
25 import android.bluetooth.BluetoothGattCallback;
26 import android.bluetooth.BluetoothGattCharacteristic;
27 import android.bluetooth.BluetoothGattDescriptor;
28 import android.bluetooth.BluetoothGattService;
29 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
30 import android.bluetooth.BluetoothLeAudioContentMetadata;
31 import android.bluetooth.BluetoothLeBroadcastAssistant;
32 import android.bluetooth.BluetoothLeBroadcastChannel;
33 import android.bluetooth.BluetoothLeBroadcastMetadata;
34 import android.bluetooth.BluetoothLeBroadcastReceiveState;
35 import android.bluetooth.BluetoothLeBroadcastSubgroup;
36 import android.bluetooth.BluetoothProfile;
37 import android.bluetooth.BluetoothStatusCodes;
38 import android.bluetooth.BluetoothUtils;
39 import android.bluetooth.BluetoothUtils.TypeValueEntry;
40 import android.bluetooth.le.PeriodicAdvertisingCallback;
41 import android.bluetooth.le.PeriodicAdvertisingReport;
42 import android.bluetooth.le.ScanRecord;
43 import android.bluetooth.le.ScanResult;
44 import android.content.Intent;
45 import android.os.Binder;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.ParcelUuid;
49 import android.provider.DeviceConfig;
50 import android.util.Log;
51 import android.util.Pair;
52 
53 import com.android.bluetooth.BluetoothMethodProxy;
54 import com.android.bluetooth.Utils;
55 import com.android.bluetooth.btservice.AdapterService;
56 import com.android.bluetooth.btservice.ProfileService;
57 import com.android.bluetooth.flags.Flags;
58 import com.android.internal.annotations.VisibleForTesting;
59 import com.android.internal.util.State;
60 import com.android.internal.util.StateMachine;
61 
62 import java.io.ByteArrayOutputStream;
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.io.StringWriter;
66 import java.nio.charset.StandardCharsets;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collection;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.List;
73 import java.util.Map;
74 import java.util.Scanner;
75 import java.util.UUID;
76 import java.util.stream.IntStream;
77 
78 @VisibleForTesting
79 public class BassClientStateMachine extends StateMachine {
80     private static final String TAG = "BassClientStateMachine";
81     @VisibleForTesting static final byte[] REMOTE_SCAN_STOP = {00};
82     @VisibleForTesting static final byte[] REMOTE_SCAN_START = {01};
83     private static final byte OPCODE_ADD_SOURCE = 0x02;
84     private static final byte OPCODE_UPDATE_SOURCE = 0x03;
85     private static final byte OPCODE_SET_BCAST_PIN = 0x04;
86     private static final byte OPCODE_REMOVE_SOURCE = 0x05;
87     private static final int UPDATE_SOURCE_FIXED_LENGTH = 6;
88 
89     static final int CONNECT = 1;
90     static final int DISCONNECT = 2;
91     static final int CONNECTION_STATE_CHANGED = 3;
92     static final int GATT_TXN_PROCESSED = 4;
93     static final int READ_BASS_CHARACTERISTICS = 5;
94     static final int START_SCAN_OFFLOAD = 6;
95     static final int STOP_SCAN_OFFLOAD = 7;
96     static final int SELECT_BCAST_SOURCE = 8;
97     static final int ADD_BCAST_SOURCE = 9;
98     static final int UPDATE_BCAST_SOURCE = 10;
99     static final int SET_BCAST_CODE = 11;
100     static final int REMOVE_BCAST_SOURCE = 12;
101     static final int GATT_TXN_TIMEOUT = 13;
102     static final int PSYNC_ACTIVE_TIMEOUT = 14;
103     static final int CONNECT_TIMEOUT = 15;
104     static final int REACHED_MAX_SOURCE_LIMIT = 16;
105     static final int SWITCH_BCAST_SOURCE = 17;
106     static final int CANCEL_PENDING_SOURCE_OPERATION = 18;
107 
108     // NOTE: the value is not "final" - it is modified in the unit tests
109     @VisibleForTesting private int mConnectTimeoutMs;
110 
111     // Type of argument for set broadcast code operation
112     static final int ARGTYPE_METADATA = 1;
113     static final int ARGTYPE_RCVSTATE = 2;
114 
115     static final int ATT_WRITE_CMD_HDR_LEN = 3;
116 
117     private final Map<Integer, PeriodicAdvertisingCallback> mPeriodicAdvCallbacksMap =
118             new HashMap<>();
119     /*key is combination of sourceId, Address and advSid for this hashmap*/
120     private final Map<Integer, BluetoothLeBroadcastReceiveState>
121             mBluetoothLeBroadcastReceiveStates =
122                     new HashMap<Integer, BluetoothLeBroadcastReceiveState>();
123     private final Map<Integer, BluetoothLeBroadcastMetadata> mCurrentMetadata = new HashMap();
124     private final Disconnected mDisconnected = new Disconnected();
125     private final Connected mConnected = new Connected();
126     private final Connecting mConnecting = new Connecting();
127     private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing();
128     private final List<Pair<ScanResult, Integer>> mSourceSyncRequestsQueue =
129             new ArrayList<Pair<ScanResult, Integer>>();
130 
131     @VisibleForTesting
132     final List<BluetoothGattCharacteristic> mBroadcastCharacteristics =
133             new ArrayList<BluetoothGattCharacteristic>();
134 
135     @VisibleForTesting BluetoothDevice mDevice;
136 
137     private boolean mIsAllowedList = false;
138     private int mLastConnectionState = -1;
139     @VisibleForTesting boolean mMTUChangeRequested = false;
140     @VisibleForTesting boolean mDiscoveryInitiated = false;
141     @VisibleForTesting BassClientService mService;
142     AdapterService mAdapterService;
143     @VisibleForTesting BluetoothGattCharacteristic mBroadcastScanControlPoint;
144     private final Map<Integer, Boolean> mFirstTimeBisDiscoveryMap;
145     private int mPASyncRetryCounter = 0;
146     @VisibleForTesting int mNumOfBroadcastReceiverStates = 0;
147     @VisibleForTesting int mPendingOperation = -1;
148     @VisibleForTesting byte mPendingSourceId = -1;
149     @VisibleForTesting BluetoothLeBroadcastMetadata mPendingMetadata = null;
150     private BluetoothLeBroadcastMetadata mSetBroadcastPINMetadata = null;
151     @VisibleForTesting boolean mSetBroadcastCodePending = false;
152     private final Map<Integer, Boolean> mPendingRemove = new HashMap();
153     @VisibleForTesting boolean mAutoTriggered = false;
154     private boolean mDefNoPAS = false;
155     private boolean mForceSB = false;
156     @VisibleForTesting BluetoothLeBroadcastMetadata mPendingSourceToAdd = null;
157     private int mBroadcastSourceIdLength = 3;
158     @VisibleForTesting byte mNextSourceId = 0;
159     private boolean mAllowReconnect = false;
160     @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null;
161     BluetoothGattCallback mGattCallback = null;
162     @VisibleForTesting PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback();
163     int mMaxSingleAttributeWriteValueLen = 0;
164     @VisibleForTesting BluetoothLeBroadcastMetadata mPendingSourceToSwitch = null;
165 
BassClientStateMachine( BluetoothDevice device, BassClientService svc, AdapterService adapterService, Looper looper, int connectTimeoutMs)166     BassClientStateMachine(
167             BluetoothDevice device,
168             BassClientService svc,
169             AdapterService adapterService,
170             Looper looper,
171             int connectTimeoutMs) {
172         super(TAG + "(" + device.toString() + ")", looper);
173         mDevice = device;
174         mService = svc;
175         mAdapterService = adapterService;
176         mConnectTimeoutMs = connectTimeoutMs;
177         addState(mDisconnected);
178         addState(mConnected);
179         addState(mConnecting);
180         addState(mConnectedProcessing);
181         setInitialState(mDisconnected);
182         mFirstTimeBisDiscoveryMap = new HashMap<Integer, Boolean>();
183         long token = Binder.clearCallingIdentity();
184         mIsAllowedList =
185                 DeviceConfig.getBoolean(
186                         DeviceConfig.NAMESPACE_BLUETOOTH, "persist.vendor.service.bt.wl", true);
187         mDefNoPAS =
188                 DeviceConfig.getBoolean(
189                         DeviceConfig.NAMESPACE_BLUETOOTH,
190                         "persist.vendor.service.bt.defNoPAS",
191                         false);
192         mForceSB =
193                 DeviceConfig.getBoolean(
194                         DeviceConfig.NAMESPACE_BLUETOOTH,
195                         "persist.vendor.service.bt.forceSB",
196                         false);
197         Binder.restoreCallingIdentity(token);
198     }
199 
make( BluetoothDevice device, BassClientService svc, AdapterService adapterService, Looper looper)200     static BassClientStateMachine make(
201             BluetoothDevice device,
202             BassClientService svc,
203             AdapterService adapterService,
204             Looper looper) {
205         Log.d(TAG, "make for device " + device);
206 
207         if (!BassClientPeriodicAdvertisingManager
208                 .initializePeriodicAdvertisingManagerOnDefaultAdapter()) {
209             Log.e(TAG, "Failed to initialize Periodic Advertising Manager on Default Adapter");
210             return null;
211         }
212 
213         BassClientStateMachine BassclientSm =
214                 new BassClientStateMachine(
215                         device, svc, adapterService, looper, BassConstants.CONNECT_TIMEOUT_MS);
216         BassclientSm.start();
217         return BassclientSm;
218     }
219 
destroy(BassClientStateMachine stateMachine)220     static void destroy(BassClientStateMachine stateMachine) {
221         Log.i(TAG, "destroy");
222         if (stateMachine == null) {
223             Log.w(TAG, "destroy(), stateMachine is null");
224             return;
225         }
226         stateMachine.doQuit();
227         stateMachine.cleanup();
228     }
229 
doQuit()230     public void doQuit() {
231         log("doQuit for device " + mDevice);
232         quitNow();
233     }
234 
cleanup()235     public void cleanup() {
236         log("cleanup for device " + mDevice);
237         clearCharsCache();
238 
239         if (mBluetoothGatt != null) {
240             log("disconnect gatt");
241             mBluetoothGatt.disconnect();
242             mBluetoothGatt.close();
243             mBluetoothGatt = null;
244             mGattCallback = null;
245         }
246         mPendingOperation = -1;
247         mPendingSourceId = -1;
248         mPendingMetadata = null;
249         mPendingSourceToAdd = null;
250         mPendingSourceToSwitch = null;
251         mCurrentMetadata.clear();
252         mPendingRemove.clear();
253         mPeriodicAdvCallbacksMap.clear();
254         mSourceSyncRequestsQueue.clear();
255     }
256 
hasPendingSourceOperation()257     Boolean hasPendingSourceOperation() {
258         return mPendingMetadata != null;
259     }
260 
hasPendingSourceOperation(int broadcastId)261     Boolean hasPendingSourceOperation(int broadcastId) {
262         return mPendingMetadata != null && mPendingMetadata.getBroadcastId() == broadcastId;
263     }
264 
cancelPendingSourceOperation(int broadcastId)265     private void cancelPendingSourceOperation(int broadcastId) {
266         if ((mPendingMetadata != null) && (mPendingMetadata.getBroadcastId() == broadcastId)) {
267             Log.d(TAG, "clearPendingSourceOperation: broadcast ID: " + broadcastId);
268             mPendingMetadata = null;
269         }
270     }
271 
getCurrentBroadcastMetadata(Integer sourceId)272     BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
273         return mCurrentMetadata.getOrDefault(sourceId, null);
274     }
275 
setCurrentBroadcastMetadata( Integer sourceId, BluetoothLeBroadcastMetadata metadata)276     private void setCurrentBroadcastMetadata(
277             Integer sourceId, BluetoothLeBroadcastMetadata metadata) {
278         if (metadata != null) {
279             mCurrentMetadata.put(sourceId, metadata);
280         } else {
281             mCurrentMetadata.remove(sourceId);
282         }
283     }
284 
isPendingRemove(Integer sourceId)285     boolean isPendingRemove(Integer sourceId) {
286         return mPendingRemove.getOrDefault(sourceId, false);
287     }
288 
setPendingRemove(Integer sourceId, boolean remove)289     private void setPendingRemove(Integer sourceId, boolean remove) {
290         if (remove) {
291             mPendingRemove.put(sourceId, remove);
292         } else {
293             mPendingRemove.remove(sourceId);
294         }
295     }
296 
getBroadcastReceiveStateForSourceDevice( BluetoothDevice srcDevice)297     BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceDevice(
298             BluetoothDevice srcDevice) {
299         List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources();
300         BluetoothLeBroadcastReceiveState state = null;
301         for (int i = 0; i < currentSources.size(); i++) {
302             BluetoothDevice device = currentSources.get(i).getSourceDevice();
303             if (device != null && device.equals(srcDevice)) {
304                 state = currentSources.get(i);
305                 Log.e(
306                         TAG,
307                         "getBroadcastReceiveStateForSourceDevice: returns for: "
308                                 + srcDevice
309                                 + "&srcInfo"
310                                 + state);
311                 return state;
312             }
313         }
314         return null;
315     }
316 
getBroadcastReceiveStateForSourceId(int sourceId)317     BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceId(int sourceId) {
318         List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources();
319         for (int i = 0; i < currentSources.size(); i++) {
320             if (sourceId == currentSources.get(i).getSourceId()) {
321                 return currentSources.get(i);
322             }
323         }
324         return null;
325     }
326 
isSyncedToTheSource(int sourceId)327     boolean isSyncedToTheSource(int sourceId) {
328         BluetoothLeBroadcastReceiveState recvState = getBroadcastReceiveStateForSourceId(sourceId);
329 
330         return recvState != null
331                 && (recvState.getPaSyncState()
332                                 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
333                         || recvState.getBisSyncState().stream()
334                                 .anyMatch(
335                                         bitmap -> {
336                                             return bitmap != 0;
337                                         }));
338     }
339 
parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData)340     void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) {
341         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
342             throw new RuntimeException(
343                     "Should never be executed with"
344                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
345         }
346         log("parseBaseData" + Arrays.toString(serviceData));
347         BaseData base = BaseData.parseBaseData(serviceData);
348         if (base != null) {
349             mService.updateBase(syncHandle, base);
350             base.print();
351             if (mAutoTriggered) {
352                 // successful auto periodic synchrnization with source
353                 log("auto triggered assist");
354                 mAutoTriggered = false;
355                 // perform PAST with this device
356                 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
357                 if (srcDevice != null) {
358                     BluetoothLeBroadcastReceiveState recvState =
359                             getBroadcastReceiveStateForSourceDevice(srcDevice);
360                     processPASyncState(recvState);
361                 } else {
362                     Log.w(TAG, "Autoassist: no matching device");
363                 }
364             }
365         } else {
366             Log.e(TAG, "Seems BASE is not in parsable format");
367             if (!mAutoTriggered) {
368                 cancelActiveSync(syncHandle);
369             } else {
370                 mAutoTriggered = false;
371             }
372         }
373     }
374 
parseScanRecord(int syncHandle, ScanRecord record)375     void parseScanRecord(int syncHandle, ScanRecord record) {
376         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
377             throw new RuntimeException(
378                     "Should never be executed with"
379                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
380         }
381         log("parseScanRecord: " + record);
382         Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData();
383         if (bmsAdvDataMap != null) {
384             for (Map.Entry<ParcelUuid, byte[]> entry : bmsAdvDataMap.entrySet()) {
385                 log(
386                         "ParcelUUid = "
387                                 + entry.getKey()
388                                 + ", Value = "
389                                 + Arrays.toString(entry.getValue()));
390             }
391         }
392         byte[] advData = record.getServiceData(BassConstants.BASIC_AUDIO_UUID);
393         if (advData != null) {
394             parseBaseData(mDevice, syncHandle, advData);
395         } else {
396             Log.e(TAG, "No service data in Scan record");
397             if (!mAutoTriggered) {
398                 cancelActiveSync(syncHandle);
399             } else {
400                 mAutoTriggered = false;
401             }
402         }
403     }
404 
checkAndParseBroadcastName(ScanRecord record)405     private String checkAndParseBroadcastName(ScanRecord record) {
406         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
407             throw new RuntimeException(
408                     "Should never be executed with"
409                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
410         }
411         log("checkAndParseBroadcastName");
412         byte[] rawBytes = record.getBytes();
413         List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes);
414         if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) {
415             Log.e(TAG, "Invalid LTV entries in Scan record");
416             return null;
417         }
418 
419         String broadcastName = null;
420         for (TypeValueEntry entry : entries) {
421             // Only use the first value of each type
422             if (broadcastName == null && entry.getType() == BassConstants.BCAST_NAME_AD_TYPE) {
423                 byte[] bytes = entry.getValue();
424                 int len = bytes.length;
425                 if (len < BassConstants.BCAST_NAME_LEN_MIN
426                         || len > BassConstants.BCAST_NAME_LEN_MAX) {
427                     Log.e(TAG, "Invalid broadcast name length in Scan record" + len);
428                     return null;
429                 }
430                 broadcastName = new String(bytes, StandardCharsets.UTF_8);
431             }
432         }
433         return broadcastName;
434     }
435 
selectSource(ScanResult scanRes, boolean autoTriggered)436     private boolean selectSource(ScanResult scanRes, boolean autoTriggered) {
437         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
438             throw new RuntimeException(
439                     "Should never be executed with"
440                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
441         }
442         log("selectSource: ScanResult " + scanRes);
443         mAutoTriggered = autoTriggered;
444         mPASyncRetryCounter = 1;
445 
446         // updating mainly for Address type and PA Interval here
447         // extract BroadcastId from ScanResult
448         ScanRecord scanRecord = scanRes.getScanRecord();
449         if (scanRecord != null) {
450             Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData();
451             int broadcastId = BassConstants.INVALID_BROADCAST_ID;
452             PublicBroadcastData pbData = null;
453             if (listOfUuids != null) {
454                 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) {
455                     byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID);
456                     broadcastId = BassUtils.parseBroadcastId(bId);
457                 }
458                 if (listOfUuids.containsKey(BassConstants.PUBLIC_BROADCAST_UUID)) {
459                     byte[] pbAnnouncement = listOfUuids.get(BassConstants.PUBLIC_BROADCAST_UUID);
460                     pbData = PublicBroadcastData.parsePublicBroadcastData(pbAnnouncement);
461                 }
462             }
463 
464             if (broadcastId == BassConstants.INVALID_BROADCAST_ID || pbData == null) {
465                 Log.w(TAG, "Invalid broadcast ID or public broadcast data");
466                 return false;
467             }
468 
469             // Check if broadcast name present in scan record and parse
470             // null if no name present
471             String broadcastName = checkAndParseBroadcastName(scanRecord);
472 
473             // Avoid duplicated sync request if the same broadcast BIG is synced
474             if (isSourceSynced(broadcastId)) {
475                 log("Skip duplicated sync request to broadcast id: " + broadcastId);
476                 return false;
477             }
478 
479             PeriodicAdvertisingCallback paCb = new PACallback();
480             // put temp sync handle and update in onSyncEstablished
481             int tempHandle = BassConstants.INVALID_SYNC_HANDLE;
482             mPeriodicAdvCallbacksMap.put(tempHandle, paCb);
483             try {
484                 BluetoothMethodProxy.getInstance()
485                         .periodicAdvertisingManagerRegisterSync(
486                                 BassClientPeriodicAdvertisingManager
487                                         .getPeriodicAdvertisingManager(),
488                                 scanRes,
489                                 0,
490                                 BassConstants.PSYNC_TIMEOUT,
491                                 paCb,
492                                 null);
493             } catch (IllegalArgumentException ex) {
494                 Log.w(TAG, "registerSync:IllegalArgumentException");
495                 mPeriodicAdvCallbacksMap.remove(tempHandle);
496                 return false;
497             }
498 
499             mService.updatePeriodicAdvertisementResultMap(
500                     scanRes.getDevice(),
501                     scanRes.getDevice().getAddressType(),
502                     BassConstants.INVALID_SYNC_HANDLE,
503                     BassConstants.INVALID_ADV_SID,
504                     scanRes.getPeriodicAdvertisingInterval(),
505                     broadcastId,
506                     pbData,
507                     broadcastName);
508         }
509         return true;
510     }
511 
isSourceSynced(int broadcastId)512     private boolean isSourceSynced(int broadcastId) {
513         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
514             throw new RuntimeException(
515                     "Should never be executed with"
516                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
517         }
518         List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice);
519         return (activeSyncedSrc != null
520                 && activeSyncedSrc.contains(mService.getSyncHandleForBroadcastId(broadcastId)));
521     }
522 
cancelActiveSync(Integer syncHandle)523     private void cancelActiveSync(Integer syncHandle) {
524         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
525             throw new RuntimeException(
526                     "Should never be executed with"
527                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
528         }
529         log("cancelActiveSync: syncHandle = " + syncHandle);
530         if (syncHandle == null) {
531             // clean up the pending sync request if syncHandle is null
532             mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE);
533         }
534         List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice);
535 
536         /* Stop sync if there is some running */
537         if (activeSyncedSrc != null
538                 && (syncHandle == null || activeSyncedSrc.contains(syncHandle))) {
539             if (syncHandle != null) {
540                 // only one source needs to be unsynced
541                 unsyncSource(syncHandle);
542                 mService.removeActiveSyncedSource(mDevice, syncHandle);
543             } else {
544                 // remove all the sources
545                 for (int handle : activeSyncedSrc) {
546                     unsyncSource(handle);
547                 }
548                 mService.removeActiveSyncedSource(mDevice, null);
549             }
550             if (mService.getActiveSyncedSources(mDevice) == null) {
551                 // all sources are removed, clean up
552                 removeMessages(PSYNC_ACTIVE_TIMEOUT);
553                 mService.clearNotifiedFlags();
554             }
555         }
556     }
557 
unsyncSource(int syncHandle)558     private boolean unsyncSource(int syncHandle) {
559         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
560             throw new RuntimeException(
561                     "Should never be executed with"
562                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
563         }
564         if (syncHandle != BassConstants.INVALID_SYNC_HANDLE
565                 && mPeriodicAdvCallbacksMap.containsKey(syncHandle)) {
566             try {
567                 BluetoothMethodProxy.getInstance()
568                         .periodicAdvertisingManagerUnregisterSync(
569                                 BassClientPeriodicAdvertisingManager
570                                         .getPeriodicAdvertisingManager(),
571                                 mPeriodicAdvCallbacksMap.get(syncHandle));
572             } catch (IllegalArgumentException ex) {
573                 Log.w(TAG, "unregisterSync:IllegalArgumentException");
574                 return false;
575             }
576             mPeriodicAdvCallbacksMap.remove(syncHandle);
577         } else {
578             log("calling unregisterSync, not found syncHandle: " + syncHandle);
579         }
580         return true;
581     }
582 
resetBluetoothGatt()583     private void resetBluetoothGatt() {
584         // cleanup mBluetoothGatt
585         if (mBluetoothGatt != null) {
586             mBluetoothGatt.close();
587             mBluetoothGatt = null;
588         }
589     }
590 
getBroadcastMetadataFromBaseData( BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted)591     private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData(
592             BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted) {
593         if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
594             throw new RuntimeException(
595                     "Should never be executed with"
596                             + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
597         }
598         BluetoothLeBroadcastMetadata.Builder metaData = new BluetoothLeBroadcastMetadata.Builder();
599         int index = 0;
600         for (BaseData.BaseInformation baseLevel2 : baseData.getLevelTwo()) {
601             BluetoothLeBroadcastSubgroup.Builder subGroup =
602                     new BluetoothLeBroadcastSubgroup.Builder();
603             for (int j = 0; j < baseLevel2.numSubGroups; j++) {
604                 BaseData.BaseInformation baseLevel3 = baseData.getLevelThree().get(index++);
605                 BluetoothLeBroadcastChannel.Builder channel =
606                         new BluetoothLeBroadcastChannel.Builder();
607                 channel.setChannelIndex(baseLevel3.index);
608                 channel.setSelected(false);
609                 try {
610                     channel.setCodecMetadata(
611                             BluetoothLeAudioCodecConfigMetadata.fromRawBytes(
612                                     baseLevel3.codecConfigInfo));
613                 } catch (IllegalArgumentException e) {
614                     Log.w(TAG, "Invalid metadata, adding empty data. Error: " + e);
615                     channel.setCodecMetadata(
616                             BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0]));
617                 }
618                 subGroup.addChannel(channel.build());
619             }
620             byte[] arrayCodecId = baseLevel2.codecId;
621             long codeId =
622                     ((long) (arrayCodecId[4] & 0xff)) << 32
623                             | (arrayCodecId[3] & 0xff) << 24
624                             | (arrayCodecId[2] & 0xff) << 16
625                             | (arrayCodecId[1] & 0xff) << 8
626                             | (arrayCodecId[0] & 0xff);
627             subGroup.setCodecId(codeId);
628             try {
629                 subGroup.setCodecSpecificConfig(
630                         BluetoothLeAudioCodecConfigMetadata.fromRawBytes(
631                                 baseLevel2.codecConfigInfo));
632             } catch (IllegalArgumentException e) {
633                 Log.w(TAG, "Invalid config, adding empty one. Error: " + e);
634                 subGroup.setCodecSpecificConfig(
635                         BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0]));
636             }
637 
638             try {
639                 subGroup.setContentMetadata(
640                         BluetoothLeAudioContentMetadata.fromRawBytes(baseLevel2.metaData));
641             } catch (IllegalArgumentException e) {
642                 Log.w(TAG, "Invalid metadata, adding empty one. Error: " + e);
643                 subGroup.setContentMetadata(
644                         BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0]));
645             }
646 
647             metaData.addSubgroup(subGroup.build());
648         }
649         metaData.setSourceDevice(device, device.getAddressType());
650         byte[] arrayPresentationDelay = baseData.getLevelOne().presentationDelay;
651         int presentationDelay =
652                 (int)
653                         ((arrayPresentationDelay[2] & 0xff) << 16
654                                 | (arrayPresentationDelay[1] & 0xff) << 8
655                                 | (arrayPresentationDelay[0] & 0xff));
656         metaData.setPresentationDelayMicros(presentationDelay);
657         PeriodicAdvertisementResult result =
658                 mService.getPeriodicAdvertisementResult(
659                         device, mService.getBroadcastIdForSyncHandle(syncHandle));
660         if (result != null) {
661             int broadcastId = result.getBroadcastId();
662             log("broadcast ID: " + broadcastId);
663             metaData.setBroadcastId(broadcastId);
664             metaData.setSourceAdvertisingSid(result.getAdvSid());
665 
666             PublicBroadcastData pbData = result.getPublicBroadcastData();
667             if (pbData != null) {
668                 metaData.setPublicBroadcast(true);
669                 metaData.setAudioConfigQuality(pbData.getAudioConfigQuality());
670                 try {
671                     metaData.setPublicBroadcastMetadata(
672                             BluetoothLeAudioContentMetadata.fromRawBytes(pbData.getMetadata()));
673                 } catch (IllegalArgumentException e) {
674                     Log.w(TAG, "Invalid public metadata, adding empty one. Error " + e);
675                     metaData.setPublicBroadcastMetadata(null);
676                 }
677             }
678 
679             String broadcastName = result.getBroadcastName();
680             if (broadcastName != null) {
681                 metaData.setBroadcastName(broadcastName);
682             }
683         }
684         metaData.setEncrypted(encrypted);
685         if (Flags.leaudioBroadcastMonitorSourceSyncStatus()) {
686             // update the rssi value
687             ScanResult scanRes = mService.getCachedBroadcast(result.getBroadcastId());
688             if (scanRes != null) {
689                 metaData.setRssi(scanRes.getRssi());
690             }
691         }
692         return metaData.build();
693     }
694 
broadcastReceiverState(BluetoothLeBroadcastReceiveState state, int sourceId)695     private void broadcastReceiverState(BluetoothLeBroadcastReceiveState state, int sourceId) {
696         log("broadcastReceiverState: " + mDevice);
697         mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state);
698     }
699 
700     @VisibleForTesting
isEmpty(final byte[] data)701     static boolean isEmpty(final byte[] data) {
702         return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0);
703     }
704 
processPASyncState(BluetoothLeBroadcastReceiveState recvState)705     private void processPASyncState(BluetoothLeBroadcastReceiveState recvState) {
706         int serviceData = 0;
707         if (recvState == null) {
708             Log.e(TAG, "processPASyncState: recvState is null");
709             return;
710         }
711         int state = recvState.getPaSyncState();
712         if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) {
713             log("Initiate PAST procedure");
714             PeriodicAdvertisementResult result =
715                     mService.getPeriodicAdvertisementResult(
716                             recvState.getSourceDevice(), recvState.getBroadcastId());
717             if (result != null) {
718                 int syncHandle = result.getSyncHandle();
719                 log("processPASyncState: syncHandle " + result.getSyncHandle());
720                 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) {
721                     serviceData = 0x000000FF & recvState.getSourceId();
722                     serviceData = serviceData << 8;
723                     // advA matches EXT_ADV_ADDRESS
724                     // also matches source address (as we would have written)
725                     serviceData =
726                             serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
727                     serviceData =
728                             serviceData
729                                     & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
730                     log(
731                             "Initiate PAST for: "
732                                     + mDevice
733                                     + ", syncHandle: "
734                                     + syncHandle
735                                     + "serviceData"
736                                     + serviceData);
737                     BluetoothMethodProxy.getInstance()
738                             .periodicAdvertisingManagerTransferSync(
739                                     BassClientPeriodicAdvertisingManager
740                                             .getPeriodicAdvertisingManager(),
741                                     mDevice,
742                                     serviceData,
743                                     syncHandle);
744                 }
745             } else {
746                 BluetoothLeBroadcastMetadata currentMetadata =
747                         getCurrentBroadcastMetadata(recvState.getSourceId());
748                 if (mService.isLocalBroadcast(currentMetadata)) {
749                     int advHandle = currentMetadata.getSourceAdvertisingSid();
750                     serviceData = 0x000000FF & recvState.getSourceId();
751                     serviceData = serviceData << 8;
752                     // Address we set in the Source Address can differ from the address in the air
753                     serviceData =
754                             serviceData | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS;
755                     log(
756                             "Initiate local broadcast PAST for: "
757                                     + mDevice
758                                     + ", advSID/Handle: "
759                                     + advHandle
760                                     + ", serviceData: "
761                                     + serviceData);
762                     BluetoothMethodProxy.getInstance()
763                             .periodicAdvertisingManagerTransferSetInfo(
764                                     BassClientPeriodicAdvertisingManager
765                                             .getPeriodicAdvertisingManager(),
766                                     mDevice,
767                                     serviceData,
768                                     advHandle,
769                                     mLocalPeriodicAdvCallback);
770                 } else {
771                     Log.e(TAG, "There is no valid sync handle for this Source");
772                 }
773             }
774         }
775     }
776 
checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState)777     private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) {
778         log("checkAndUpdateBroadcastCode");
779         // non colocated case, Broadcast PIN should have been updated from lyaer
780         // If there is pending one process it Now
781         if (recvState.getBigEncryptionState()
782                         == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED
783                 && mSetBroadcastCodePending) {
784             log("Update the Broadcast now");
785             if (mSetBroadcastPINMetadata != null) {
786                 setCurrentBroadcastMetadata(recvState.getSourceId(), mSetBroadcastPINMetadata);
787             }
788             Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
789             m.obj = recvState;
790             m.arg1 = ARGTYPE_RCVSTATE;
791             sendMessage(m);
792             mSetBroadcastCodePending = false;
793             mSetBroadcastPINMetadata = null;
794         }
795     }
796 
parseBroadcastReceiverState(byte[] receiverState)797     private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(byte[] receiverState) {
798         byte sourceId = 0;
799         if (receiverState.length > 0) {
800             sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX];
801         }
802         log("processBroadcastReceiverState: receiverState length: " + receiverState.length);
803 
804         BluetoothLeBroadcastReceiveState recvState = null;
805         if (receiverState.length == 0
806                 || isEmpty(Arrays.copyOfRange(receiverState, 1, receiverState.length - 1))) {
807             byte[] emptyBluetoothDeviceAddress = Utils.getBytesFromAddress("00:00:00:00:00:00");
808             if (mPendingOperation == REMOVE_BCAST_SOURCE) {
809                 recvState =
810                         new BluetoothLeBroadcastReceiveState(
811                                 mPendingSourceId,
812                                 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType
813                                 mAdapterService.getDeviceFromByte(
814                                         emptyBluetoothDeviceAddress), // sourceDev
815                                 0, // sourceAdvertisingSid
816                                 0, // broadcastId
817                                 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState
818                                 // bigEncryptionState
819                                 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
820                                 null, // badCode
821                                 0, // numSubgroups
822                                 Arrays.asList(new Long[0]), // bisSyncState
823                                 Arrays.asList(
824                                         new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata
825                                 );
826             } else if (receiverState.length == 0) {
827                 if (mBluetoothLeBroadcastReceiveStates != null) {
828                     mNextSourceId = (byte) mBluetoothLeBroadcastReceiveStates.size();
829                 }
830                 if (mNextSourceId >= mNumOfBroadcastReceiverStates) {
831                     Log.e(TAG, "reached the remote supported max SourceInfos");
832                     return null;
833                 }
834                 mNextSourceId++;
835                 recvState =
836                         new BluetoothLeBroadcastReceiveState(
837                                 mNextSourceId,
838                                 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType
839                                 mAdapterService.getDeviceFromByte(
840                                         emptyBluetoothDeviceAddress), // sourceDev
841                                 0, // sourceAdvertisingSid
842                                 0, // broadcastId
843                                 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState
844                                 // bigEncryptionState
845                                 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
846                                 null, // badCode
847                                 0, // numSubgroups
848                                 Arrays.asList(new Long[0]), // bisSyncState
849                                 Arrays.asList(
850                                         new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata
851                                 );
852             }
853         } else {
854             byte paSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX];
855             byte bigEncryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX];
856             byte[] badBroadcastCode = null;
857             int badBroadcastCodeLen = 0;
858             if (bigEncryptionStatus
859                     == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) {
860                 badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE];
861                 System.arraycopy(
862                         receiverState,
863                         BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX,
864                         badBroadcastCode,
865                         0,
866                         BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE);
867                 badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE;
868             }
869             byte numSubGroups =
870                     receiverState[
871                             BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen];
872             int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen + 1;
873             ArrayList<BluetoothLeAudioContentMetadata> metadataList =
874                     new ArrayList<BluetoothLeAudioContentMetadata>();
875             ArrayList<Long> bisSyncState = new ArrayList<Long>();
876             for (int i = 0; i < numSubGroups; i++) {
877                 byte[] bisSyncIndex = new byte[BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE];
878                 System.arraycopy(
879                         receiverState,
880                         offset,
881                         bisSyncIndex,
882                         0,
883                         BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE);
884                 offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE;
885                 bisSyncState.add((long) Utils.byteArrayToInt(bisSyncIndex));
886 
887                 int metaDataLength = receiverState[offset++] & 0xFF;
888                 if (metaDataLength > 0) {
889                     log("metadata of length: " + metaDataLength + "is available");
890                     byte[] metaData = new byte[metaDataLength];
891                     System.arraycopy(receiverState, offset, metaData, 0, metaDataLength);
892                     offset += metaDataLength;
893                     metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData));
894                 } else {
895                     metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0]));
896                 }
897             }
898             byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength];
899             System.arraycopy(
900                     receiverState,
901                     BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX,
902                     broadcastIdBytes,
903                     0,
904                     mBroadcastSourceIdLength);
905             int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes);
906             byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE];
907             System.arraycopy(
908                     receiverState,
909                     BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX,
910                     sourceAddress,
911                     0,
912                     BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE);
913             byte sourceAddressType =
914                     receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX];
915             BassUtils.reverse(sourceAddress);
916             String address = Utils.getAddressStringFromByte(sourceAddress);
917             BluetoothDevice device =
918                     BluetoothAdapter.getDefaultAdapter()
919                             .getRemoteLeDevice(address, sourceAddressType);
920             byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX];
921             recvState =
922                     new BluetoothLeBroadcastReceiveState(
923                             sourceId,
924                             (int) sourceAddressType,
925                             device,
926                             sourceAdvSid,
927                             broadcastId,
928                             (int) paSyncState,
929                             (int) bigEncryptionStatus,
930                             badBroadcastCode,
931                             numSubGroups,
932                             bisSyncState,
933                             metadataList);
934         }
935         return recvState;
936     }
937 
processBroadcastReceiverState( byte[] receiverState, BluetoothGattCharacteristic characteristic)938     private void processBroadcastReceiverState(
939             byte[] receiverState, BluetoothGattCharacteristic characteristic) {
940         log("processBroadcastReceiverState: characteristic:" + characteristic);
941         BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState(receiverState);
942         if (recvState == null) {
943             log("processBroadcastReceiverState: Null recvState");
944             return;
945         } else if (recvState.getSourceId() == -1) {
946             log("processBroadcastReceiverState: invalid index: " + recvState.getSourceId());
947             return;
948         }
949         BluetoothLeBroadcastReceiveState oldRecvState =
950                 mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId());
951         if (oldRecvState == null) {
952             log("Initial Read and Populating values");
953             if (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates) {
954                 Log.e(TAG, "reached the Max SourceInfos");
955                 return;
956             }
957             mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState);
958             checkAndUpdateBroadcastCode(recvState);
959             processPASyncState(recvState);
960         } else {
961             log("Updated receiver state: " + recvState);
962             mBluetoothLeBroadcastReceiveStates.replace(characteristic.getInstanceId(), recvState);
963             String emptyBluetoothDevice = "00:00:00:00:00:00";
964             if (oldRecvState.getSourceDevice() == null
965                     || oldRecvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) {
966                 log("New Source Addition");
967                 removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
968                 mService.getCallbacks()
969                         .notifySourceAdded(
970                                 mDevice, recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
971                 if (mPendingMetadata != null) {
972                     setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata);
973                     mPendingMetadata = null;
974                 }
975                 checkAndUpdateBroadcastCode(recvState);
976                 processPASyncState(recvState);
977             } else {
978                 if (recvState.getSourceDevice() == null
979                         || recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) {
980                     BluetoothDevice removedDevice = oldRecvState.getSourceDevice();
981                     log("sourceInfo removal " + removedDevice);
982                     if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
983                         cancelActiveSync(
984                                 mService.getSyncHandleForBroadcastId(recvState.getBroadcastId()));
985                     }
986                     setCurrentBroadcastMetadata(oldRecvState.getSourceId(), null);
987                     if (mPendingSourceToSwitch != null) {
988                         // Source remove is triggered by switch source request
989                         mService.getCallbacks()
990                                 .notifySourceRemoved(
991                                         mDevice,
992                                         oldRecvState.getSourceId(),
993                                         BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);
994                         log("Switching to new source");
995                         Message message = obtainMessage(ADD_BCAST_SOURCE);
996                         message.obj = mPendingSourceToSwitch;
997                         sendMessage(message);
998                         mPendingSourceToSwitch = null;
999                     } else {
1000                         mService.getCallbacks()
1001                                 .notifySourceRemoved(
1002                                         mDevice,
1003                                         oldRecvState.getSourceId(),
1004                                         BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
1005                     }
1006                 } else {
1007                     log("update to an existing recvState");
1008                     if (mPendingMetadata != null) {
1009                         setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata);
1010                         mPendingMetadata = null;
1011                     }
1012                     removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
1013                     mService.getCallbacks()
1014                             .notifySourceModified(
1015                                     mDevice,
1016                                     recvState.getSourceId(),
1017                                     BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
1018                     checkAndUpdateBroadcastCode(recvState);
1019                     processPASyncState(recvState);
1020 
1021                     if (isPendingRemove(recvState.getSourceId())) {
1022                         Message message = obtainMessage(REMOVE_BCAST_SOURCE);
1023                         message.arg1 = recvState.getSourceId();
1024                         sendMessage(message);
1025                     }
1026                 }
1027             }
1028         }
1029         broadcastReceiverState(recvState, recvState.getSourceId());
1030     }
1031 
1032     // Implements callback methods for GATT events that the app cares about.
1033     // For example, connection change and services discovered.
1034     final class GattCallback extends BluetoothGattCallback {
1035         @Override
onConnectionStateChange(BluetoothGatt gatt, int status, int newState)1036         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
1037             boolean isStateChanged = false;
1038             log("onConnectionStateChange : Status=" + status + "newState" + newState);
1039             if (newState == BluetoothProfile.STATE_CONNECTED
1040                     && getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
1041                 isStateChanged = true;
1042                 Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice);
1043                 if (mService.okToConnect(mDevice)) {
1044                     log("Bassclient Connected to: " + mDevice);
1045                     if (mBluetoothGatt != null) {
1046                         log(
1047                                 "Attempting to start service discovery:"
1048                                         + mBluetoothGatt.discoverServices());
1049                         mDiscoveryInitiated = true;
1050                     }
1051                 } else if (mBluetoothGatt != null) {
1052                     // Reject the connection
1053                     Log.w(TAG, "Bassclient Connect request rejected: " + mDevice);
1054                     mBluetoothGatt.disconnect();
1055                     mBluetoothGatt.close();
1056                     mBluetoothGatt = null;
1057                     // force move to disconnected
1058                     newState = BluetoothProfile.STATE_DISCONNECTED;
1059                 }
1060             } else if (newState == BluetoothProfile.STATE_DISCONNECTED
1061                     && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
1062                 isStateChanged = true;
1063                 log("Disconnected from Bass GATT server.");
1064             }
1065             if (isStateChanged) {
1066                 Message m = obtainMessage(CONNECTION_STATE_CHANGED);
1067                 m.obj = newState;
1068                 sendMessage(m);
1069             }
1070         }
1071 
1072         @Override
onServicesDiscovered(BluetoothGatt gatt, int status)1073         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
1074             log("onServicesDiscovered:" + status);
1075             if (mDiscoveryInitiated) {
1076                 mDiscoveryInitiated = false;
1077                 if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) {
1078                     mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES);
1079                     mMTUChangeRequested = true;
1080                 } else {
1081                     Log.w(
1082                             TAG,
1083                             "onServicesDiscovered received: "
1084                                     + status
1085                                     + "mBluetoothGatt"
1086                                     + mBluetoothGatt);
1087                 }
1088             } else {
1089                 log("remote initiated callback");
1090             }
1091         }
1092 
1093         @Override
onCharacteristicRead( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)1094         public void onCharacteristicRead(
1095                 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
1096             if (status == BluetoothGatt.GATT_SUCCESS
1097                     && characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
1098                 log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status);
1099                 if (characteristic.getValue() == null) {
1100                     Log.e(TAG, "Remote receiver state is NULL");
1101                     return;
1102                 }
1103                 logByteArray(
1104                         "Received ",
1105                         characteristic.getValue(),
1106                         0,
1107                         characteristic.getValue().length);
1108                 processBroadcastReceiverState(characteristic.getValue(), characteristic);
1109             }
1110             // switch to receiving notifications after initial characteristic read
1111             BluetoothGattDescriptor desc =
1112                     characteristic.getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG);
1113             if (mBluetoothGatt != null && desc != null) {
1114                 log("Setting the value for Desc");
1115                 mBluetoothGatt.setCharacteristicNotification(characteristic, true);
1116                 desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
1117                 mBluetoothGatt.writeDescriptor(desc);
1118             } else {
1119                 Log.w(TAG, "CCC for " + characteristic + "seem to be not present");
1120                 // at least move the SM to stable state
1121                 Message m = obtainMessage(GATT_TXN_PROCESSED);
1122                 m.arg1 = status;
1123                 sendMessage(m);
1124             }
1125         }
1126 
1127         @Override
onDescriptorWrite( BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)1128         public void onDescriptorWrite(
1129                 BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
1130             // Move the SM to connected so further reads happens
1131             Message m = obtainMessage(GATT_TXN_PROCESSED);
1132             m.arg1 = status;
1133             sendMessage(m);
1134         }
1135 
1136         @Override
onMtuChanged(BluetoothGatt gatt, int mtu, int status)1137         public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
1138             if (mMTUChangeRequested && mBluetoothGatt != null) {
1139                 acquireAllBassChars();
1140                 mMTUChangeRequested = false;
1141             } else {
1142                 log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" + mBluetoothGatt);
1143             }
1144 
1145             if (status == BluetoothGatt.GATT_SUCCESS) {
1146                 Log.d(TAG, "mtu: " + mtu);
1147                 mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN;
1148             }
1149         }
1150 
1151         @Override
onCharacteristicChanged( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)1152         public void onCharacteristicChanged(
1153                 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
1154             if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
1155                 if (characteristic.getValue() == null) {
1156                     Log.e(TAG, "Remote receiver state is NULL");
1157                     return;
1158                 }
1159                 processBroadcastReceiverState(characteristic.getValue(), characteristic);
1160             }
1161         }
1162 
1163         @Override
onCharacteristicWrite( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)1164         public void onCharacteristicWrite(
1165                 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
1166             Message m = obtainMessage(GATT_TXN_PROCESSED);
1167             m.arg1 = status;
1168             sendMessage(m);
1169         }
1170     }
1171 
1172     /** Internal periodc Advertising manager callback */
1173     private final class PACallback extends PeriodicAdvertisingCallback {
1174         @Override
onSyncEstablished( int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status)1175         public void onSyncEstablished(
1176                 int syncHandle,
1177                 BluetoothDevice device,
1178                 int advertisingSid,
1179                 int skip,
1180                 int timeout,
1181                 int status) {
1182             if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1183                 throw new RuntimeException(
1184                         "Should never be executed with"
1185                                 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
1186             }
1187             log(
1188                     "onSyncEstablished syncHandle: "
1189                             + syncHandle
1190                             + ", device: "
1191                             + device
1192                             + ", advertisingSid: "
1193                             + advertisingSid
1194                             + ", skip: "
1195                             + skip
1196                             + ", timeout: "
1197                             + timeout
1198                             + ", status: "
1199                             + status);
1200             if (status == BluetoothGatt.GATT_SUCCESS) {
1201                 // updates syncHandle, advSid
1202                 // set other fields as invalid or null
1203                 mService.updatePeriodicAdvertisementResultMap(
1204                         device,
1205                         BassConstants.INVALID_ADV_ADDRESS_TYPE,
1206                         syncHandle,
1207                         advertisingSid,
1208                         BassConstants.INVALID_ADV_INTERVAL,
1209                         BassConstants.INVALID_BROADCAST_ID,
1210                         null,
1211                         null);
1212                 removeMessages(PSYNC_ACTIVE_TIMEOUT);
1213                 // Refresh sync timeout if another source synced
1214                 sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT, BassConstants.PSYNC_ACTIVE_TIMEOUT_MS);
1215                 mService.addActiveSyncedSource(mDevice, syncHandle);
1216 
1217                 // update valid sync handle in mPeriodicAdvCallbacksMap
1218                 if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) {
1219                     PeriodicAdvertisingCallback paCb =
1220                             mPeriodicAdvCallbacksMap.get(BassConstants.INVALID_SYNC_HANDLE);
1221                     mPeriodicAdvCallbacksMap.put(syncHandle, paCb);
1222                     mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE);
1223                 }
1224                 mFirstTimeBisDiscoveryMap.put(syncHandle, true);
1225                 if (mPendingSourceToAdd != null) {
1226                     Message message = obtainMessage(ADD_BCAST_SOURCE);
1227                     message.obj = mPendingSourceToAdd;
1228                     sendMessage(message);
1229                 }
1230             } else {
1231                 log("failed to sync to PA: " + mPASyncRetryCounter);
1232                 mAutoTriggered = false;
1233                 // remove failed sync handle
1234                 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE);
1235             }
1236             mPendingSourceToAdd = null;
1237             if (!mSourceSyncRequestsQueue.isEmpty()) {
1238                 log("Processing the next source to sync");
1239                 Pair<ScanResult, Integer> queuedSourceToSync = mSourceSyncRequestsQueue.remove(0);
1240                 Message msg = obtainMessage(SELECT_BCAST_SOURCE);
1241                 msg.obj = queuedSourceToSync.first;
1242                 msg.arg1 = queuedSourceToSync.second;
1243                 sendMessage(msg);
1244             }
1245         }
1246 
1247         @Override
onPeriodicAdvertisingReport(PeriodicAdvertisingReport report)1248         public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
1249             if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1250                 throw new RuntimeException(
1251                         "Should never be executed with"
1252                                 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
1253             }
1254             log("onPeriodicAdvertisingReport");
1255             Boolean first = mFirstTimeBisDiscoveryMap.get(report.getSyncHandle());
1256             // Parse the BIS indices from report's service data
1257             if (first != null && first.booleanValue() == true) {
1258                 parseScanRecord(report.getSyncHandle(), report.getData());
1259                 mFirstTimeBisDiscoveryMap.put(report.getSyncHandle(), false);
1260             }
1261         }
1262 
1263         @Override
onSyncLost(int syncHandle)1264         public void onSyncLost(int syncHandle) {
1265             if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1266                 throw new RuntimeException(
1267                         "Should never be executed with"
1268                                 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
1269             }
1270             log("OnSyncLost" + syncHandle);
1271             if (Flags.leaudioBroadcastMonitorSourceSyncStatus()) {
1272                 int broadcastId = mService.getBroadcastIdForSyncHandle(syncHandle);
1273                 if (broadcastId != BassConstants.INVALID_BROADCAST_ID) {
1274                     log("Notify broadcast source lost, broadcast id: " + broadcastId);
1275                     mService.getCallbacks().notifySourceLost(broadcastId);
1276                 }
1277             }
1278             cancelActiveSync(syncHandle);
1279         }
1280 
1281         @Override
onBigInfoAdvertisingReport(int syncHandle, boolean encrypted)1282         public void onBigInfoAdvertisingReport(int syncHandle, boolean encrypted) {
1283             if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1284                 throw new RuntimeException(
1285                         "Should never be executed with"
1286                                 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag");
1287             }
1288             log(
1289                     "onBIGInfoAdvertisingReport: syncHandle="
1290                             + syncHandle
1291                             + ", encrypted ="
1292                             + encrypted);
1293             BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
1294             if (srcDevice == null) {
1295                 log("No device found.");
1296                 return;
1297             }
1298             PeriodicAdvertisementResult result =
1299                     mService.getPeriodicAdvertisementResult(
1300                             srcDevice, mService.getBroadcastIdForSyncHandle(syncHandle));
1301             if (result == null) {
1302                 log("No PA record found");
1303                 return;
1304             }
1305             if (!result.isNotified()) {
1306                 result.setNotified(true);
1307                 BaseData baseData = mService.getBase(syncHandle);
1308                 if (baseData == null) {
1309                     log("No BaseData found");
1310                     return;
1311                 }
1312                 BluetoothLeBroadcastMetadata metaData =
1313                         getBroadcastMetadataFromBaseData(
1314                                 baseData, srcDevice, syncHandle, encrypted);
1315                 log("Notify broadcast source found");
1316                 mService.getCallbacks().notifySourceFound(metaData);
1317             }
1318         }
1319 
1320         @Override
onSyncTransferred(BluetoothDevice device, int status)1321         public void onSyncTransferred(BluetoothDevice device, int status) {
1322             log("onSyncTransferred: device=" + device + ", status =" + status);
1323         }
1324     }
1325 
1326     /**
1327      * Connects to the GATT server of the device.
1328      *
1329      * @return {@code true} if it successfully connects to the GATT server.
1330      */
1331     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
connectGatt(Boolean autoConnect)1332     public boolean connectGatt(Boolean autoConnect) {
1333         if (mGattCallback == null) {
1334             mGattCallback = new GattCallback();
1335         }
1336 
1337         BluetoothGatt gatt =
1338                 mDevice.connectGatt(
1339                         mService,
1340                         autoConnect,
1341                         mGattCallback,
1342                         BluetoothDevice.TRANSPORT_LE,
1343                         (BluetoothDevice.PHY_LE_1M_MASK
1344                                 | BluetoothDevice.PHY_LE_2M_MASK
1345                                 | BluetoothDevice.PHY_LE_CODED_MASK),
1346                         null);
1347 
1348         if (gatt != null) {
1349             mBluetoothGatt = new BluetoothGattTestableWrapper(gatt);
1350         }
1351 
1352         return mBluetoothGatt != null;
1353     }
1354 
1355     /** getAllSources */
getAllSources()1356     public List<BluetoothLeBroadcastReceiveState> getAllSources() {
1357         List list = new ArrayList(mBluetoothLeBroadcastReceiveStates.values());
1358         return list;
1359     }
1360 
acquireAllBassChars()1361     void acquireAllBassChars() {
1362         clearCharsCache();
1363         BluetoothGattService service = null;
1364         if (mBluetoothGatt != null) {
1365             log("getting Bass Service handle");
1366             service = mBluetoothGatt.getService(BassConstants.BASS_UUID);
1367         }
1368         if (service == null) {
1369             log("acquireAllBassChars: BASS service not found");
1370             return;
1371         }
1372         log("found BASS_SERVICE");
1373         List<BluetoothGattCharacteristic> allChars = service.getCharacteristics();
1374         int numOfChars = allChars.size();
1375         mNumOfBroadcastReceiverStates = numOfChars - 1;
1376         log("Total number of chars" + numOfChars);
1377         for (int i = 0; i < allChars.size(); i++) {
1378             if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
1379                 int properties = allChars.get(i).getProperties();
1380 
1381                 if (((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0)
1382                         || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0)) {
1383                     Log.w(
1384                             TAG,
1385                             "Broadcast Audio Scan Control Point characteristic has invalid "
1386                                     + "properties!");
1387                 } else {
1388                     mBroadcastScanControlPoint = allChars.get(i);
1389                     log("Index of ScanCtrlPoint:" + i);
1390                 }
1391             } else {
1392                 log("Reading " + i + "th ReceiverState");
1393                 mBroadcastCharacteristics.add(allChars.get(i));
1394                 Message m = obtainMessage(READ_BASS_CHARACTERISTICS);
1395                 m.obj = allChars.get(i);
1396                 sendMessage(m);
1397             }
1398         }
1399     }
1400 
clearCharsCache()1401     void clearCharsCache() {
1402         if (mBroadcastCharacteristics != null) {
1403             mBroadcastCharacteristics.clear();
1404         }
1405         if (mBroadcastScanControlPoint != null) {
1406             mBroadcastScanControlPoint = null;
1407         }
1408         mNumOfBroadcastReceiverStates = 0;
1409         if (mBluetoothLeBroadcastReceiveStates != null) {
1410             mBluetoothLeBroadcastReceiveStates.clear();
1411         }
1412         mPendingOperation = -1;
1413         mPendingMetadata = null;
1414         mCurrentMetadata.clear();
1415         mPendingRemove.clear();
1416     }
1417 
1418     @VisibleForTesting
1419     class Disconnected extends State {
1420         @Override
enter()1421         public void enter() {
1422             log(
1423                     "Enter Disconnected("
1424                             + mDevice
1425                             + "): "
1426                             + messageWhatToString(getCurrentMessage().what));
1427             clearCharsCache();
1428             mNextSourceId = 0;
1429             removeDeferredMessages(DISCONNECT);
1430             if (mLastConnectionState == -1) {
1431                 log("no Broadcast of initial profile state ");
1432             } else {
1433                 broadcastConnectionState(
1434                         mDevice, mLastConnectionState, BluetoothProfile.STATE_DISCONNECTED);
1435                 if (mLastConnectionState != BluetoothProfile.STATE_DISCONNECTED) {
1436                     // Reconnect in background if not disallowed by the service
1437                     if (mService.okToConnect(mDevice) && mAllowReconnect) {
1438                         connectGatt(true);
1439                     }
1440                 }
1441             }
1442         }
1443 
1444         @Override
exit()1445         public void exit() {
1446             log(
1447                     "Exit Disconnected("
1448                             + mDevice
1449                             + "): "
1450                             + messageWhatToString(getCurrentMessage().what));
1451             mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
1452         }
1453 
1454         @Override
processMessage(Message message)1455         public boolean processMessage(Message message) {
1456             log(
1457                     "Disconnected process message("
1458                             + mDevice
1459                             + "): "
1460                             + messageWhatToString(message.what));
1461             switch (message.what) {
1462                 case CONNECT:
1463                     log("Connecting to " + mDevice);
1464                     if (mBluetoothGatt != null) {
1465                         Log.d(TAG, "clear off, pending wl connection");
1466                         mBluetoothGatt.disconnect();
1467                         mBluetoothGatt.close();
1468                         mBluetoothGatt = null;
1469                     }
1470                     mAllowReconnect = true;
1471                     if (connectGatt(mIsAllowedList)) {
1472                         transitionTo(mConnecting);
1473                     } else {
1474                         Log.e(TAG, "Disconnected: error connecting to " + mDevice);
1475                     }
1476                     break;
1477                 case DISCONNECT:
1478                     // Disconnect if there's an ongoing background connection
1479                     mAllowReconnect = false;
1480                     if (mBluetoothGatt != null) {
1481                         log("Cancelling the background connection to " + mDevice);
1482                         mBluetoothGatt.disconnect();
1483                         mBluetoothGatt.close();
1484                         mBluetoothGatt = null;
1485                     } else {
1486                         Log.d(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
1487                     }
1488                     break;
1489                 case CONNECTION_STATE_CHANGED:
1490                     int state = (int) message.obj;
1491                     Log.w(TAG, "connection state changed:" + state);
1492                     if (state == BluetoothProfile.STATE_CONNECTED) {
1493                         log("remote/wl connection");
1494                         transitionTo(mConnected);
1495                     } else {
1496                         Log.w(TAG, "Disconnected: Connection failed to " + mDevice);
1497                     }
1498                     break;
1499                 case PSYNC_ACTIVE_TIMEOUT:
1500                     cancelActiveSync(null);
1501                     break;
1502                 default:
1503                     log("DISCONNECTED: not handled message:" + message.what);
1504                     return NOT_HANDLED;
1505             }
1506             return HANDLED;
1507         }
1508     }
1509 
1510     @VisibleForTesting
1511     class Connecting extends State {
1512         @Override
enter()1513         public void enter() {
1514             log(
1515                     "Enter Connecting("
1516                             + mDevice
1517                             + "): "
1518                             + messageWhatToString(getCurrentMessage().what));
1519             sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs);
1520             broadcastConnectionState(
1521                     mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTING);
1522         }
1523 
1524         @Override
exit()1525         public void exit() {
1526             log(
1527                     "Exit Connecting("
1528                             + mDevice
1529                             + "): "
1530                             + messageWhatToString(getCurrentMessage().what));
1531             mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
1532             removeMessages(CONNECT_TIMEOUT);
1533         }
1534 
1535         @Override
processMessage(Message message)1536         public boolean processMessage(Message message) {
1537             log(
1538                     "Connecting process message("
1539                             + mDevice
1540                             + "): "
1541                             + messageWhatToString(message.what));
1542             switch (message.what) {
1543                 case CONNECT:
1544                     log("Already Connecting to " + mDevice);
1545                     log("Ignore this connection request " + mDevice);
1546                     break;
1547                 case DISCONNECT:
1548                     Log.w(TAG, "Connecting: DISCONNECT deferred: " + mDevice);
1549                     deferMessage(message);
1550                     break;
1551                 case READ_BASS_CHARACTERISTICS:
1552                     Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
1553                     deferMessage(message);
1554                     break;
1555                 case CONNECTION_STATE_CHANGED:
1556                     int state = (int) message.obj;
1557                     Log.w(TAG, "Connecting: connection state changed:" + state);
1558                     if (state == BluetoothProfile.STATE_CONNECTED) {
1559                         transitionTo(mConnected);
1560                     } else {
1561                         Log.w(TAG, "Connection failed to " + mDevice);
1562                         resetBluetoothGatt();
1563                         transitionTo(mDisconnected);
1564                     }
1565                     break;
1566                 case CONNECT_TIMEOUT:
1567                     Log.w(TAG, "CONNECT_TIMEOUT");
1568                     BluetoothDevice device = (BluetoothDevice) message.obj;
1569                     if (!mDevice.equals(device)) {
1570                         Log.e(TAG, "Unknown device timeout " + device);
1571                         break;
1572                     }
1573                     resetBluetoothGatt();
1574                     transitionTo(mDisconnected);
1575                     break;
1576                 case PSYNC_ACTIVE_TIMEOUT:
1577                     deferMessage(message);
1578                     break;
1579                 default:
1580                     log("CONNECTING: not handled message:" + message.what);
1581                     return NOT_HANDLED;
1582             }
1583             return HANDLED;
1584         }
1585     }
1586 
getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels)1587     private static int getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels) {
1588         int bisSync = 0;
1589         for (BluetoothLeBroadcastChannel channel : channels) {
1590             if (channel.isSelected()) {
1591                 if (channel.getChannelIndex() == 0) {
1592                     Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0");
1593                     continue;
1594                 }
1595                 bisSync |= 1 << (channel.getChannelIndex() - 1);
1596             }
1597         }
1598 
1599         return bisSync;
1600     }
1601 
convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData)1602     private byte[] convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData) {
1603         ByteArrayOutputStream stream = new ByteArrayOutputStream();
1604         BluetoothDevice advSource = metaData.getSourceDevice();
1605 
1606         // Opcode
1607         stream.write(OPCODE_ADD_SOURCE);
1608 
1609         // Advertiser_Address_Type
1610         stream.write(metaData.getSourceAddressType());
1611 
1612         // Advertiser_Address
1613         byte[] bcastSourceAddr = Utils.getBytesFromAddress(advSource.getAddress());
1614         BassUtils.reverse(bcastSourceAddr);
1615         stream.write(bcastSourceAddr, 0, 6);
1616 
1617         // Advertising_SID
1618         stream.write(metaData.getSourceAdvertisingSid());
1619 
1620         // Broadcast_ID
1621         stream.write(metaData.getBroadcastId() & 0x00000000000000FF);
1622         stream.write((metaData.getBroadcastId() & 0x000000000000FF00) >>> 8);
1623         stream.write((metaData.getBroadcastId() & 0x0000000000FF0000) >>> 16);
1624 
1625         // PA_Sync
1626         if (mDefNoPAS) {
1627             // Synchronize to PA – PAST not available
1628             stream.write(0x02);
1629         } else {
1630             // Synchronize to PA – PAST available
1631             stream.write(0x01);
1632         }
1633 
1634         // PA_Interval
1635         stream.write((metaData.getPaSyncInterval() & 0x00000000000000FF));
1636         stream.write((metaData.getPaSyncInterval() & 0x000000000000FF00) >>> 8);
1637 
1638         // Num_Subgroups
1639         List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups();
1640         stream.write(metaData.getSubgroups().size());
1641 
1642         for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
1643             // BIS_Sync
1644             int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
1645             if (bisSync == 0) {
1646                 bisSync = 0xFFFFFFFF;
1647             }
1648             stream.write(bisSync & 0x00000000000000FF);
1649             stream.write((bisSync & 0x000000000000FF00) >>> 8);
1650             stream.write((bisSync & 0x0000000000FF0000) >>> 16);
1651             stream.write((bisSync & 0x00000000FF000000) >>> 24);
1652 
1653             // Metadata_Length
1654             BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata();
1655             stream.write(metadata.getRawMetadata().length);
1656 
1657             // Metadata
1658             stream.write(metadata.getRawMetadata(), 0, metadata.getRawMetadata().length);
1659         }
1660 
1661         byte[] res = stream.toByteArray();
1662         BassUtils.printByteArray(res);
1663         return res;
1664     }
1665 
convertBroadcastMetadataToUpdateSourceByteArray( int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync)1666     private byte[] convertBroadcastMetadataToUpdateSourceByteArray(
1667             int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync) {
1668         BluetoothLeBroadcastReceiveState existingState =
1669                 getBroadcastReceiveStateForSourceId(sourceId);
1670         if (existingState == null) {
1671             log("no existing SI for update source op");
1672             return null;
1673         }
1674         List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups();
1675         byte numSubGroups = (byte) subGroups.size();
1676         byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
1677         int offset = 0;
1678         // Opcode
1679         res[offset++] = OPCODE_UPDATE_SOURCE;
1680         // Source_ID
1681         res[offset++] = (byte) sourceId;
1682         // PA_Sync
1683         if (paSync != BassConstants.INVALID_PA_SYNC_VALUE) {
1684             res[offset++] = (byte) paSync;
1685         } else if (existingState.getPaSyncState()
1686                 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
1687             res[offset++] = (byte) (0x01);
1688         } else {
1689             res[offset++] = (byte) 0x00;
1690         }
1691         // PA_Interval
1692         res[offset++] = (byte) 0xFF;
1693         res[offset++] = (byte) 0xFF;
1694         // Num_Subgroups
1695         res[offset++] = numSubGroups;
1696 
1697         for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
1698             int bisIndexValue;
1699             if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
1700                 bisIndexValue = 0;
1701             } else if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
1702                     || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) {
1703                 bisIndexValue = getBisSyncFromChannelPreference(subGroup.getChannels());
1704 
1705                 // Let sink decide to which BIS sync if there is no channel preference
1706                 if (bisIndexValue == 0) {
1707                     bisIndexValue = 0xFFFFFFFF;
1708                 }
1709             } else {
1710                 bisIndexValue =
1711                         existingState.getBisSyncState().get(subGroups.indexOf(subGroup)).intValue();
1712             }
1713             log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
1714             // BIS_Sync
1715             res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
1716             res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
1717             res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
1718             res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
1719             // Metadata_Length; On Modify source, don't update any Metadata
1720             res[offset++] = 0;
1721         }
1722         log("UPDATE_BCAST_SOURCE in Bytes");
1723         BassUtils.printByteArray(res);
1724         return res;
1725     }
1726 
convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState)1727     private byte[] convertRecvStateToSetBroadcastCodeByteArray(
1728             BluetoothLeBroadcastReceiveState recvState) {
1729         byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN];
1730         // Opcode
1731         res[0] = OPCODE_SET_BCAST_PIN;
1732         // Source_ID
1733         res[1] = (byte) recvState.getSourceId();
1734         log(
1735                 "convertRecvStateToSetBroadcastCodeByteArray: Source device : "
1736                         + recvState.getSourceDevice());
1737         BluetoothLeBroadcastMetadata metaData =
1738                 getCurrentBroadcastMetadata(recvState.getSourceId());
1739         if (metaData == null) {
1740             Log.e(TAG, "Fail to find broadcast source, sourceId = " + recvState.getSourceId());
1741             return null;
1742         }
1743         // Broadcast Code
1744         byte[] actualPIN = metaData.getBroadcastCode();
1745         if (actualPIN == null) {
1746             Log.e(TAG, "actual PIN is null");
1747             return null;
1748         } else {
1749             log("byte array broadcast Code:" + Arrays.toString(actualPIN));
1750             log("pinLength:" + actualPIN.length);
1751             // Broadcast_Code, Fill the PIN code in the Last Position
1752             // This effectively adds padding zeros to MSB positions when the broadcast code
1753             // is shorter than 16 octets, skip the first 2 bytes for opcode and source_id.
1754             System.arraycopy(actualPIN, 0, res, 2, actualPIN.length);
1755             log("SET_BCAST_PIN in Bytes");
1756             BassUtils.printByteArray(res);
1757         }
1758         return res;
1759     }
1760 
isItRightTimeToUpdateBroadcastPin(byte sourceId)1761     private boolean isItRightTimeToUpdateBroadcastPin(byte sourceId) {
1762         Collection<BluetoothLeBroadcastReceiveState> recvStates =
1763                 mBluetoothLeBroadcastReceiveStates.values();
1764         Iterator<BluetoothLeBroadcastReceiveState> iterator = recvStates.iterator();
1765         boolean retval = false;
1766         if (mForceSB) {
1767             log("force SB is set");
1768             return true;
1769         }
1770         while (iterator.hasNext()) {
1771             BluetoothLeBroadcastReceiveState state = iterator.next();
1772             if (state == null) {
1773                 log("Source state is null");
1774                 continue;
1775             }
1776             if (sourceId == state.getSourceId()
1777                     && state.getBigEncryptionState()
1778                             == BluetoothLeBroadcastReceiveState
1779                                     .BIG_ENCRYPTION_STATE_CODE_REQUIRED) {
1780                 retval = true;
1781                 break;
1782             }
1783         }
1784         log("IsItRightTimeToUpdateBroadcastPIN returning:" + retval);
1785         return retval;
1786     }
1787 
1788     @VisibleForTesting
1789     class Connected extends State {
1790         @Override
enter()1791         public void enter() {
1792             log(
1793                     "Enter Connected("
1794                             + mDevice
1795                             + "): "
1796                             + messageWhatToString(getCurrentMessage().what));
1797             removeDeferredMessages(CONNECT);
1798             if (mLastConnectionState != BluetoothProfile.STATE_CONNECTED) {
1799                 broadcastConnectionState(
1800                         mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTED);
1801             }
1802         }
1803 
1804         @Override
exit()1805         public void exit() {
1806             log(
1807                     "Exit Connected("
1808                             + mDevice
1809                             + "): "
1810                             + messageWhatToString(getCurrentMessage().what));
1811             mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
1812         }
1813 
writeBassControlPoint(byte[] value)1814         private void writeBassControlPoint(byte[] value) {
1815             if (value.length > mMaxSingleAttributeWriteValueLen) {
1816                 mBroadcastScanControlPoint.setWriteType(
1817                         BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
1818             } else {
1819                 mBroadcastScanControlPoint.setWriteType(
1820                         BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
1821             }
1822 
1823             mBroadcastScanControlPoint.setValue(value);
1824             mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1825         }
1826 
1827         @Override
processMessage(Message message)1828         public boolean processMessage(Message message) {
1829             log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what));
1830             BluetoothLeBroadcastMetadata metaData;
1831             switch (message.what) {
1832                 case CONNECT:
1833                     Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
1834                     break;
1835                 case DISCONNECT:
1836                     log("Disconnecting from " + mDevice);
1837                     mAllowReconnect = false;
1838                     if (mBluetoothGatt != null) {
1839                         mService.handleDeviceDisconnection(mDevice, true);
1840                         mBluetoothGatt.disconnect();
1841                         mBluetoothGatt.close();
1842                         mBluetoothGatt = null;
1843                         if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1844                             cancelActiveSync(null);
1845                         }
1846                         transitionTo(mDisconnected);
1847                     } else {
1848                         log("mBluetoothGatt is null");
1849                     }
1850                     break;
1851                 case CONNECTION_STATE_CHANGED:
1852                     int state = (int) message.obj;
1853                     Log.w(TAG, "Connected:connection state changed:" + state);
1854                     if (state == BluetoothProfile.STATE_CONNECTED) {
1855                         Log.w(TAG, "device is already connected to Bass" + mDevice);
1856                     } else {
1857                         Log.w(TAG, "unexpected disconnected from " + mDevice);
1858                         mService.handleDeviceDisconnection(mDevice, false);
1859                         resetBluetoothGatt();
1860                         if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1861                             cancelActiveSync(null);
1862                         }
1863                         transitionTo(mDisconnected);
1864                     }
1865                     break;
1866                 case READ_BASS_CHARACTERISTICS:
1867                     BluetoothGattCharacteristic characteristic =
1868                             (BluetoothGattCharacteristic) message.obj;
1869                     if (mBluetoothGatt != null) {
1870                         mBluetoothGatt.readCharacteristic(characteristic);
1871                         transitionTo(mConnectedProcessing);
1872                     } else {
1873                         Log.e(TAG, "READ_BASS_CHARACTERISTICS is ignored, Gatt handle is null");
1874                     }
1875                     break;
1876                 case START_SCAN_OFFLOAD:
1877                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1878                         writeBassControlPoint(REMOTE_SCAN_START);
1879                         mPendingOperation = message.what;
1880                         transitionTo(mConnectedProcessing);
1881                     } else {
1882                         log("no Bluetooth Gatt handle, may need to fetch write");
1883                     }
1884                     break;
1885                 case STOP_SCAN_OFFLOAD:
1886                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1887                         writeBassControlPoint(REMOTE_SCAN_STOP);
1888                         mPendingOperation = message.what;
1889                         transitionTo(mConnectedProcessing);
1890                     } else {
1891                         log("no Bluetooth Gatt handle, may need to fetch write");
1892                     }
1893                     break;
1894                 case SELECT_BCAST_SOURCE:
1895                     if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1896                         throw new RuntimeException(
1897                                 "Should never be executed with"
1898                                         + " leaudioBroadcastExtractPeriodicScannerFromStateMachine"
1899                                         + " flag");
1900                     }
1901                     ScanResult scanRes = (ScanResult) message.obj;
1902                     boolean auto = ((int) message.arg1) == BassConstants.AUTO;
1903                     // check if invalid sync handle exists indicating a pending sync request
1904                     if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) {
1905                         log(
1906                                 "SELECT_BCAST_SOURCE queued due to waiting for a previous sync"
1907                                         + " response");
1908                         mSourceSyncRequestsQueue.add(
1909                                 new Pair<ScanResult, Integer>(scanRes, message.arg1));
1910                     } else {
1911                         selectSource(scanRes, auto);
1912                     }
1913                     break;
1914                 case REACHED_MAX_SOURCE_LIMIT:
1915                     if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1916                         throw new RuntimeException(
1917                                 "Should never be executed with"
1918                                         + " leaudioBroadcastExtractPeriodicScannerFromStateMachine"
1919                                         + " flag");
1920                     }
1921                     int handle = message.arg1;
1922                     cancelActiveSync(handle);
1923                     break;
1924                 case SWITCH_BCAST_SOURCE:
1925                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
1926                     int sourceIdToRemove = message.arg1;
1927                     // Save pending source to be added once existing source got removed
1928                     mPendingSourceToSwitch = metaData;
1929                     // Remove the source first
1930                     BluetoothLeBroadcastMetadata metaDataToUpdate =
1931                             getCurrentBroadcastMetadata(sourceIdToRemove);
1932                     if (metaDataToUpdate != null && isSyncedToTheSource(sourceIdToRemove)) {
1933                         log("SWITCH_BCAST_SOURCE force source to lost PA sync");
1934                         Message msg = obtainMessage(UPDATE_BCAST_SOURCE);
1935                         msg.arg1 = sourceIdToRemove;
1936                         msg.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC;
1937                         msg.obj = metaDataToUpdate;
1938                         /* Pending remove set. Remove source once not synchronized to PA */
1939                         sendMessage(msg);
1940                     } else {
1941                         Message msg = obtainMessage(REMOVE_BCAST_SOURCE);
1942                         msg.arg1 = sourceIdToRemove;
1943                         sendMessage(msg);
1944                     }
1945                     break;
1946                 case ADD_BCAST_SOURCE:
1947                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
1948 
1949                     if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
1950                         List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice);
1951                         BluetoothDevice sourceDevice = metaData.getSourceDevice();
1952                         if (!mService.isLocalBroadcast(metaData)
1953                                 && (activeSyncedSrc == null
1954                                         || !activeSyncedSrc.contains(
1955                                                 mService.getSyncHandleForBroadcastId(
1956                                                         metaData.getBroadcastId())))) {
1957                             log("Adding inactive source: " + sourceDevice);
1958                             int broadcastId = metaData.getBroadcastId();
1959                             if (broadcastId != BassConstants.INVALID_BROADCAST_ID
1960                                     && mService.getCachedBroadcast(broadcastId) != null) {
1961                                 // If the source has been synced before, try to re-sync(auto/true)
1962                                 // with the source by previously cached scan result
1963                                 Message msg = obtainMessage(SELECT_BCAST_SOURCE);
1964                                 msg.obj = mService.getCachedBroadcast(broadcastId);
1965                                 msg.arg1 = BassConstants.AUTO;
1966                                 sendMessage(msg);
1967                                 mPendingSourceToAdd = metaData;
1968                             } else {
1969                                 mService.getCallbacks()
1970                                         .notifySourceAddFailed(
1971                                                 mDevice,
1972                                                 metaData,
1973                                                 BluetoothStatusCodes.ERROR_UNKNOWN);
1974                             }
1975                             break;
1976                         }
1977                     }
1978 
1979                     byte[] addSourceInfo = convertMetadataToAddSourceByteArray(metaData);
1980                     if (addSourceInfo == null) {
1981                         Log.e(TAG, "add source: source Info is NULL");
1982                         break;
1983                     }
1984                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1985                         writeBassControlPoint(addSourceInfo);
1986                         mPendingOperation = message.what;
1987                         mPendingMetadata = metaData;
1988                         if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
1989                             mSetBroadcastCodePending = true;
1990                         }
1991                         transitionTo(mConnectedProcessing);
1992                         sendMessageDelayed(
1993                                 GATT_TXN_TIMEOUT,
1994                                 ADD_BCAST_SOURCE,
1995                                 BassConstants.GATT_TXN_TIMEOUT_MS);
1996                         sendMessageDelayed(
1997                                 CANCEL_PENDING_SOURCE_OPERATION,
1998                                 metaData.getBroadcastCode(),
1999                                 BassConstants.SOURCE_OPERATION_TIMEOUT_MS);
2000                     } else {
2001                         Log.e(TAG, "ADD_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
2002                         mService.getCallbacks()
2003                                 .notifySourceAddFailed(
2004                                         mDevice, metaData, BluetoothStatusCodes.ERROR_UNKNOWN);
2005                     }
2006                     break;
2007                 case UPDATE_BCAST_SOURCE:
2008                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
2009                     int sourceId = message.arg1;
2010                     int paSync = message.arg2;
2011                     log("Updating Broadcast source: " + metaData);
2012                     byte[] updateSourceInfo =
2013                             convertBroadcastMetadataToUpdateSourceByteArray(
2014                                     sourceId, metaData, paSync);
2015                     if (updateSourceInfo == null) {
2016                         Log.e(TAG, "update source: source Info is NULL");
2017                         break;
2018                     }
2019                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
2020                         writeBassControlPoint(updateSourceInfo);
2021                         mPendingOperation = message.what;
2022                         mPendingSourceId = (byte) sourceId;
2023                         if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
2024                             setPendingRemove(sourceId, true);
2025                         }
2026                         if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
2027                             mSetBroadcastCodePending = true;
2028                         }
2029                         mPendingMetadata = metaData;
2030                         transitionTo(mConnectedProcessing);
2031                         sendMessageDelayed(
2032                                 GATT_TXN_TIMEOUT,
2033                                 UPDATE_BCAST_SOURCE,
2034                                 BassConstants.GATT_TXN_TIMEOUT_MS);
2035                         sendMessageDelayed(
2036                                 CANCEL_PENDING_SOURCE_OPERATION,
2037                                 metaData.getBroadcastCode(),
2038                                 BassConstants.SOURCE_OPERATION_TIMEOUT_MS);
2039                     } else {
2040                         Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
2041                         mService.getCallbacks()
2042                                 .notifySourceModifyFailed(
2043                                         mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
2044                     }
2045                     break;
2046                 case SET_BCAST_CODE:
2047                     int argType = message.arg1;
2048                     mSetBroadcastCodePending = false;
2049                     BluetoothLeBroadcastReceiveState recvState = null;
2050                     if (argType == ARGTYPE_METADATA) {
2051                         mSetBroadcastPINMetadata = (BluetoothLeBroadcastMetadata) message.obj;
2052                         mSetBroadcastCodePending = true;
2053                     } else {
2054                         recvState = (BluetoothLeBroadcastReceiveState) message.obj;
2055                         if (!isItRightTimeToUpdateBroadcastPin((byte) recvState.getSourceId())) {
2056                             mSetBroadcastCodePending = true;
2057                         }
2058                     }
2059                     if (mSetBroadcastCodePending == true) {
2060                         log("Ignore SET_BCAST now, but restore it for later");
2061                         break;
2062                     }
2063                     byte[] setBroadcastPINcmd =
2064                             convertRecvStateToSetBroadcastCodeByteArray(recvState);
2065                     if (setBroadcastPINcmd == null) {
2066                         Log.e(TAG, "SET_BCAST_CODE: Broadcast code is NULL");
2067                         break;
2068                     }
2069                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
2070                         writeBassControlPoint(setBroadcastPINcmd);
2071                         mPendingOperation = message.what;
2072                         mPendingSourceId = (byte) recvState.getSourceId();
2073                         transitionTo(mConnectedProcessing);
2074                         sendMessageDelayed(
2075                                 GATT_TXN_TIMEOUT,
2076                                 SET_BCAST_CODE,
2077                                 BassConstants.GATT_TXN_TIMEOUT_MS);
2078                     }
2079                     break;
2080                 case REMOVE_BCAST_SOURCE:
2081                     byte sid = (byte) message.arg1;
2082                     log("Removing Broadcast source, sourceId: " + sid);
2083                     byte[] removeSourceInfo = new byte[2];
2084                     removeSourceInfo[0] = OPCODE_REMOVE_SOURCE;
2085                     removeSourceInfo[1] = sid;
2086                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
2087                         if (isPendingRemove((int) sid)) {
2088                             setPendingRemove((int) sid, false);
2089                         }
2090 
2091                         writeBassControlPoint(removeSourceInfo);
2092                         mPendingOperation = message.what;
2093                         mPendingSourceId = sid;
2094                         transitionTo(mConnectedProcessing);
2095                         sendMessageDelayed(
2096                                 GATT_TXN_TIMEOUT,
2097                                 REMOVE_BCAST_SOURCE,
2098                                 BassConstants.GATT_TXN_TIMEOUT_MS);
2099                     } else {
2100                         Log.e(TAG, "REMOVE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
2101                         mService.getCallbacks()
2102                                 .notifySourceRemoveFailed(
2103                                         mDevice, sid, BluetoothStatusCodes.ERROR_UNKNOWN);
2104                         if (mPendingSourceToSwitch != null) {
2105                             // Switching source failed
2106                             // Need to notify add source failure for service to cleanup
2107                             mService.getCallbacks()
2108                                     .notifySourceAddFailed(
2109                                             mDevice,
2110                                             mPendingSourceToSwitch,
2111                                             BluetoothStatusCodes.ERROR_UNKNOWN);
2112                             mPendingSourceToSwitch = null;
2113                         }
2114                     }
2115                     break;
2116                 case PSYNC_ACTIVE_TIMEOUT:
2117                     cancelActiveSync(null);
2118                     break;
2119                 case CANCEL_PENDING_SOURCE_OPERATION:
2120                     int broadcastId = message.arg1;
2121                     cancelPendingSourceOperation(broadcastId);
2122                     break;
2123                 default:
2124                     log("CONNECTED: not handled message:" + message.what);
2125                     return NOT_HANDLED;
2126             }
2127             return HANDLED;
2128         }
2129     }
2130 
isSuccess(int status)2131     private boolean isSuccess(int status) {
2132         boolean ret = false;
2133         switch (status) {
2134             case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST:
2135             case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST:
2136             case BluetoothStatusCodes.REASON_REMOTE_REQUEST:
2137             case BluetoothStatusCodes.REASON_SYSTEM_POLICY:
2138                 ret = true;
2139                 break;
2140             default:
2141                 break;
2142         }
2143         return ret;
2144     }
2145 
sendPendingCallbacks(int pendingOp, int status)2146     void sendPendingCallbacks(int pendingOp, int status) {
2147         switch (pendingOp) {
2148             case START_SCAN_OFFLOAD:
2149                 // Do not want to cancel sync because one remote does not receive START_SCAN_OFFLOAD
2150                 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
2151                     if (!isSuccess(status)) {
2152                         if (!mAutoTriggered) {
2153                             cancelActiveSync(null);
2154                         } else {
2155                             mAutoTriggered = false;
2156                         }
2157                     }
2158                 }
2159                 break;
2160             case ADD_BCAST_SOURCE:
2161                 if (!isSuccess(status)) {
2162                     if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
2163                         cancelActiveSync(null);
2164                     }
2165                     if (mPendingMetadata != null) {
2166                         mService.getCallbacks()
2167                                 .notifySourceAddFailed(mDevice, mPendingMetadata, status);
2168                         mPendingMetadata = null;
2169                     }
2170                     removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
2171                 }
2172                 break;
2173             case UPDATE_BCAST_SOURCE:
2174                 if (!mAutoTriggered
2175                         || Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
2176                     if (!isSuccess(status)) {
2177                         mService.getCallbacks()
2178                                 .notifySourceModifyFailed(mDevice, mPendingSourceId, status);
2179                         mPendingMetadata = null;
2180                         removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
2181                     }
2182                 } else {
2183                     mAutoTriggered = false;
2184                 }
2185                 break;
2186             case REMOVE_BCAST_SOURCE:
2187                 if (!isSuccess(status)) {
2188                     mService.getCallbacks()
2189                             .notifySourceRemoveFailed(mDevice, mPendingSourceId, status);
2190                     if (mPendingSourceToSwitch != null) {
2191                         // Switching source failed
2192                         // Need to notify add source failure for service to cleanup
2193                         mService.getCallbacks()
2194                                 .notifySourceAddFailed(mDevice, mPendingSourceToSwitch, status);
2195                         mPendingSourceToSwitch = null;
2196                     }
2197                 }
2198                 break;
2199             case SET_BCAST_CODE:
2200                 log("sendPendingCallbacks: SET_BCAST_CODE");
2201                 break;
2202             default:
2203                 log("sendPendingCallbacks: unhandled case");
2204                 break;
2205         }
2206     }
2207 
2208     // public for testing, but private for non-testing
2209     @VisibleForTesting
2210     class ConnectedProcessing extends State {
2211         @Override
enter()2212         public void enter() {
2213             log(
2214                     "Enter ConnectedProcessing("
2215                             + mDevice
2216                             + "): "
2217                             + messageWhatToString(getCurrentMessage().what));
2218         }
2219 
2220         @Override
exit()2221         public void exit() {
2222             /* Pending Metadata will be used to bond with source ID in receiver state notify */
2223             if (mPendingOperation == REMOVE_BCAST_SOURCE) {
2224                 mPendingMetadata = null;
2225             }
2226 
2227             log(
2228                     "Exit ConnectedProcessing("
2229                             + mDevice
2230                             + "): "
2231                             + messageWhatToString(getCurrentMessage().what));
2232         }
2233 
2234         @Override
processMessage(Message message)2235         public boolean processMessage(Message message) {
2236             log(
2237                     "ConnectedProcessing process message("
2238                             + mDevice
2239                             + "): "
2240                             + messageWhatToString(message.what));
2241             switch (message.what) {
2242                 case CONNECT:
2243                     Log.w(TAG, "CONNECT request is ignored" + mDevice);
2244                     break;
2245                 case DISCONNECT:
2246                     Log.w(TAG, "DISCONNECT requested!: " + mDevice);
2247                     mAllowReconnect = false;
2248                     if (mBluetoothGatt != null) {
2249                         mService.handleDeviceDisconnection(mDevice, true);
2250                         mBluetoothGatt.disconnect();
2251                         mBluetoothGatt.close();
2252                         mBluetoothGatt = null;
2253                         if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
2254                             cancelActiveSync(null);
2255                         }
2256                         transitionTo(mDisconnected);
2257                     } else {
2258                         log("mBluetoothGatt is null");
2259                     }
2260                     break;
2261                 case READ_BASS_CHARACTERISTICS:
2262                     Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
2263                     deferMessage(message);
2264                     break;
2265                 case CONNECTION_STATE_CHANGED:
2266                     int state = (int) message.obj;
2267                     Log.w(TAG, "ConnectedProcessing: connection state changed:" + state);
2268                     if (state == BluetoothProfile.STATE_CONNECTED) {
2269                         Log.w(TAG, "should never happen from this state");
2270                     } else {
2271                         Log.w(TAG, "Unexpected disconnection " + mDevice);
2272                         mService.handleDeviceDisconnection(mDevice, false);
2273                         resetBluetoothGatt();
2274                         if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
2275                             cancelActiveSync(null);
2276                         }
2277                         transitionTo(mDisconnected);
2278                     }
2279                     break;
2280                 case GATT_TXN_PROCESSED:
2281                     removeMessages(GATT_TXN_TIMEOUT);
2282                     int status = (int) message.arg1;
2283                     log("GATT transaction processed for" + mDevice);
2284                     if (status == BluetoothGatt.GATT_SUCCESS) {
2285                         sendPendingCallbacks(
2286                                 mPendingOperation, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
2287                     } else {
2288                         sendPendingCallbacks(mPendingOperation, BluetoothStatusCodes.ERROR_UNKNOWN);
2289                     }
2290                     transitionTo(mConnected);
2291                     break;
2292                 case GATT_TXN_TIMEOUT:
2293                     log("GATT transaction timeout for" + mDevice);
2294                     sendPendingCallbacks(mPendingOperation, BluetoothStatusCodes.ERROR_UNKNOWN);
2295                     mPendingOperation = -1;
2296                     mPendingSourceId = -1;
2297                     if ((message.arg1 == UPDATE_BCAST_SOURCE)
2298                             || (message.arg1 == ADD_BCAST_SOURCE)) {
2299                         mPendingMetadata = null;
2300                     }
2301                     transitionTo(mConnected);
2302                     break;
2303                 case START_SCAN_OFFLOAD:
2304                 case STOP_SCAN_OFFLOAD:
2305                 case SELECT_BCAST_SOURCE:
2306                 case ADD_BCAST_SOURCE:
2307                 case SET_BCAST_CODE:
2308                 case REMOVE_BCAST_SOURCE:
2309                 case REACHED_MAX_SOURCE_LIMIT:
2310                 case SWITCH_BCAST_SOURCE:
2311                 case PSYNC_ACTIVE_TIMEOUT:
2312                     log(
2313                             "defer the message: "
2314                                     + messageWhatToString(message.what)
2315                                     + ", so that it will be processed later");
2316                     deferMessage(message);
2317                     break;
2318                 case CANCEL_PENDING_SOURCE_OPERATION:
2319                     int broadcastId = message.arg1;
2320                     cancelPendingSourceOperation(broadcastId);
2321                     break;
2322                 default:
2323                     log("CONNECTEDPROCESSING: not handled message:" + message.what);
2324                     return NOT_HANDLED;
2325             }
2326             return HANDLED;
2327         }
2328     }
2329 
broadcastConnectionState(BluetoothDevice device, int fromState, int toState)2330     void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
2331         log("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
2332         if (fromState == BluetoothProfile.STATE_CONNECTED
2333                 && toState == BluetoothProfile.STATE_CONNECTED) {
2334             log("CONNECTED->CONNECTED: Ignore");
2335             return;
2336         }
2337 
2338         mService.handleConnectionStateChanged(device, fromState, toState);
2339         Intent intent = new Intent(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
2340         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
2341         intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
2342         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
2343         intent.addFlags(
2344                 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2345                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
2346         mService.sendBroadcast(
2347                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
2348     }
2349 
getConnectionState()2350     int getConnectionState() {
2351         String currentState = "Unknown";
2352         if (getCurrentState() != null) {
2353             currentState = getCurrentState().getName();
2354         }
2355         switch (currentState) {
2356             case "Disconnected":
2357                 return BluetoothProfile.STATE_DISCONNECTED;
2358             case "Connecting":
2359                 return BluetoothProfile.STATE_CONNECTING;
2360             case "Connected":
2361             case "ConnectedProcessing":
2362                 return BluetoothProfile.STATE_CONNECTED;
2363             default:
2364                 Log.e(TAG, "Bad currentState: " + currentState);
2365                 return BluetoothProfile.STATE_DISCONNECTED;
2366         }
2367     }
2368 
getMaximumSourceCapacity()2369     int getMaximumSourceCapacity() {
2370         return mNumOfBroadcastReceiverStates;
2371     }
2372 
getDevice()2373     BluetoothDevice getDevice() {
2374         return mDevice;
2375     }
2376 
isConnected()2377     synchronized boolean isConnected() {
2378         return (getCurrentState() == mConnected) || (getCurrentState() == mConnectedProcessing);
2379     }
2380 
messageWhatToString(int what)2381     public static String messageWhatToString(int what) {
2382         switch (what) {
2383             case CONNECT:
2384                 return "CONNECT";
2385             case DISCONNECT:
2386                 return "DISCONNECT";
2387             case CONNECTION_STATE_CHANGED:
2388                 return "CONNECTION_STATE_CHANGED";
2389             case GATT_TXN_PROCESSED:
2390                 return "GATT_TXN_PROCESSED";
2391             case READ_BASS_CHARACTERISTICS:
2392                 return "READ_BASS_CHARACTERISTICS";
2393             case START_SCAN_OFFLOAD:
2394                 return "START_SCAN_OFFLOAD";
2395             case STOP_SCAN_OFFLOAD:
2396                 return "STOP_SCAN_OFFLOAD";
2397             case ADD_BCAST_SOURCE:
2398                 return "ADD_BCAST_SOURCE";
2399             case SELECT_BCAST_SOURCE:
2400                 return "SELECT_BCAST_SOURCE";
2401             case UPDATE_BCAST_SOURCE:
2402                 return "UPDATE_BCAST_SOURCE";
2403             case SET_BCAST_CODE:
2404                 return "SET_BCAST_CODE";
2405             case REMOVE_BCAST_SOURCE:
2406                 return "REMOVE_BCAST_SOURCE";
2407             case REACHED_MAX_SOURCE_LIMIT:
2408                 return "REACHED_MAX_SOURCE_LIMIT";
2409             case SWITCH_BCAST_SOURCE:
2410                 return "SWITCH_BCAST_SOURCE";
2411             case PSYNC_ACTIVE_TIMEOUT:
2412                 return "PSYNC_ACTIVE_TIMEOUT";
2413             case CONNECT_TIMEOUT:
2414                 return "CONNECT_TIMEOUT";
2415             case CANCEL_PENDING_SOURCE_OPERATION:
2416                 return "CANCEL_PENDING_SOURCE_OPERATION";
2417             default:
2418                 break;
2419         }
2420         return Integer.toString(what);
2421     }
2422 
2423     /** Dump info */
dump(StringBuilder sb)2424     public void dump(StringBuilder sb) {
2425         ProfileService.println(sb, "mDevice: " + mDevice);
2426         ProfileService.println(sb, "  StateMachine: " + this);
2427         // Dump the state machine logs
2428         StringWriter stringWriter = new StringWriter();
2429         PrintWriter printWriter = new PrintWriter(stringWriter);
2430         super.dump(new FileDescriptor(), printWriter, new String[] {});
2431         printWriter.flush();
2432         stringWriter.flush();
2433         ProfileService.println(sb, "  StateMachineLog:");
2434         Scanner scanner = new Scanner(stringWriter.toString());
2435         while (scanner.hasNextLine()) {
2436             String line = scanner.nextLine();
2437             ProfileService.println(sb, "    " + line);
2438         }
2439         scanner.close();
2440         for (Map.Entry<Integer, BluetoothLeBroadcastReceiveState> entry :
2441                 mBluetoothLeBroadcastReceiveStates.entrySet()) {
2442             BluetoothLeBroadcastReceiveState state = entry.getValue();
2443             sb.append(state);
2444         }
2445     }
2446 
2447     @Override
log(String msg)2448     protected void log(String msg) {
2449         super.log(msg);
2450     }
2451 
logByteArray(String prefix, byte[] value, int offset, int count)2452     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
2453         StringBuilder builder = new StringBuilder(prefix);
2454         for (int i = offset; i < count; i++) {
2455             builder.append(String.format("0x%02X", value[i]));
2456             if (i != value.length - 1) {
2457                 builder.append(", ");
2458             }
2459         }
2460         Log.d(TAG, builder.toString());
2461     }
2462 
2463     /** Mockable wrapper of {@link BluetoothGatt}. */
2464     @VisibleForTesting
2465     public static class BluetoothGattTestableWrapper {
2466         public final BluetoothGatt mWrappedBluetoothGatt;
2467 
BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt)2468         BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt) {
2469             mWrappedBluetoothGatt = bluetoothGatt;
2470         }
2471 
2472         /** See {@link BluetoothGatt#getServices()}. */
getServices()2473         public List<BluetoothGattService> getServices() {
2474             return mWrappedBluetoothGatt.getServices();
2475         }
2476 
2477         /** See {@link BluetoothGatt#getService(UUID)}. */
2478         @Nullable
getService(UUID uuid)2479         public BluetoothGattService getService(UUID uuid) {
2480             return mWrappedBluetoothGatt.getService(uuid);
2481         }
2482 
2483         /** See {@link BluetoothGatt#discoverServices()}. */
discoverServices()2484         public boolean discoverServices() {
2485             return mWrappedBluetoothGatt.discoverServices();
2486         }
2487 
2488         /** See {@link BluetoothGatt#readCharacteristic( BluetoothGattCharacteristic)}. */
readCharacteristic(BluetoothGattCharacteristic characteristic)2489         public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
2490             return mWrappedBluetoothGatt.readCharacteristic(characteristic);
2491         }
2492 
2493         /**
2494          * See {@link BluetoothGatt#writeCharacteristic( BluetoothGattCharacteristic, byte[], int)}
2495          * .
2496          */
writeCharacteristic(BluetoothGattCharacteristic characteristic)2497         public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
2498             return mWrappedBluetoothGatt.writeCharacteristic(characteristic);
2499         }
2500 
2501         /** See {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */
readDescriptor(BluetoothGattDescriptor descriptor)2502         public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
2503             return mWrappedBluetoothGatt.readDescriptor(descriptor);
2504         }
2505 
2506         /** See {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}. */
writeDescriptor(BluetoothGattDescriptor descriptor)2507         public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
2508             return mWrappedBluetoothGatt.writeDescriptor(descriptor);
2509         }
2510 
2511         /** See {@link BluetoothGatt#requestMtu(int)}. */
requestMtu(int mtu)2512         public boolean requestMtu(int mtu) {
2513             return mWrappedBluetoothGatt.requestMtu(mtu);
2514         }
2515 
2516         /** See {@link BluetoothGatt#setCharacteristicNotification}. */
setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)2517         public boolean setCharacteristicNotification(
2518                 BluetoothGattCharacteristic characteristic, boolean enable) {
2519             return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable);
2520         }
2521 
2522         /** See {@link BluetoothGatt#disconnect()}. */
disconnect()2523         public void disconnect() {
2524             mWrappedBluetoothGatt.disconnect();
2525         }
2526 
2527         /** See {@link BluetoothGatt#close()}. */
close()2528         public void close() {
2529             mWrappedBluetoothGatt.close();
2530         }
2531     }
2532 }
2533