• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.le.PeriodicAdvertisingCallback;
39 import android.bluetooth.le.PeriodicAdvertisingManager;
40 import android.bluetooth.le.PeriodicAdvertisingReport;
41 import android.bluetooth.le.ScanRecord;
42 import android.bluetooth.le.ScanResult;
43 import android.content.Intent;
44 import android.os.Binder;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.os.ParcelUuid;
48 import android.provider.DeviceConfig;
49 import android.util.Log;
50 
51 import com.android.bluetooth.BluetoothMethodProxy;
52 import com.android.bluetooth.Utils;
53 import com.android.bluetooth.btservice.ProfileService;
54 import com.android.bluetooth.btservice.ServiceFactory;
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.internal.util.State;
57 import com.android.internal.util.StateMachine;
58 
59 import java.io.ByteArrayOutputStream;
60 import java.io.FileDescriptor;
61 import java.io.PrintWriter;
62 import java.io.StringWriter;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Collection;
66 import java.util.HashMap;
67 import java.util.Iterator;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Scanner;
71 import java.util.UUID;
72 import java.util.stream.IntStream;
73 
74 @VisibleForTesting
75 public class BassClientStateMachine extends StateMachine {
76     private static final String TAG = "BassClientStateMachine";
77     @VisibleForTesting
78     static final byte[] REMOTE_SCAN_STOP = {00};
79     @VisibleForTesting
80     static final byte[] REMOTE_SCAN_START = {01};
81     private static final byte OPCODE_ADD_SOURCE = 0x02;
82     private static final byte OPCODE_UPDATE_SOURCE = 0x03;
83     private static final byte OPCODE_SET_BCAST_PIN = 0x04;
84     private static final byte OPCODE_REMOVE_SOURCE = 0x05;
85     private static final int ADD_SOURCE_FIXED_LENGTH = 16;
86     private static final int UPDATE_SOURCE_FIXED_LENGTH = 6;
87 
88     static final int CONNECT = 1;
89     static final int DISCONNECT = 2;
90     static final int CONNECTION_STATE_CHANGED = 3;
91     static final int GATT_TXN_PROCESSED = 4;
92     static final int READ_BASS_CHARACTERISTICS = 5;
93     static final int START_SCAN_OFFLOAD = 6;
94     static final int STOP_SCAN_OFFLOAD = 7;
95     static final int SELECT_BCAST_SOURCE = 8;
96     static final int ADD_BCAST_SOURCE = 9;
97     static final int UPDATE_BCAST_SOURCE = 10;
98     static final int SET_BCAST_CODE = 11;
99     static final int REMOVE_BCAST_SOURCE = 12;
100     static final int GATT_TXN_TIMEOUT = 13;
101     static final int PSYNC_ACTIVE_TIMEOUT = 14;
102     static final int CONNECT_TIMEOUT = 15;
103 
104     // NOTE: the value is not "final" - it is modified in the unit tests
105     @VisibleForTesting
106     private int mConnectTimeoutMs;
107 
108     /*key is combination of sourceId, Address and advSid for this hashmap*/
109     private final Map<Integer, BluetoothLeBroadcastReceiveState>
110             mBluetoothLeBroadcastReceiveStates =
111             new HashMap<Integer, BluetoothLeBroadcastReceiveState>();
112     private final Map<Integer, BluetoothLeBroadcastMetadata> mCurrentMetadata = new HashMap();
113     private final Disconnected mDisconnected = new Disconnected();
114     private final Connected mConnected = new Connected();
115     private final Connecting mConnecting = new Connecting();
116     private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing();
117     @VisibleForTesting
118     final List<BluetoothGattCharacteristic> mBroadcastCharacteristics =
119             new ArrayList<BluetoothGattCharacteristic>();
120     @VisibleForTesting
121     BluetoothDevice mDevice;
122 
123     private boolean mIsAllowedList = false;
124     private int mLastConnectionState = -1;
125     @VisibleForTesting
126     boolean mMTUChangeRequested = false;
127     @VisibleForTesting
128     boolean mDiscoveryInitiated = false;
129     @VisibleForTesting
130     BassClientService mService;
131     @VisibleForTesting
132     BluetoothGattCharacteristic mBroadcastScanControlPoint;
133     private boolean mFirstTimeBisDiscovery = false;
134     private int mPASyncRetryCounter = 0;
135     private ScanResult mScanRes = null;
136     @VisibleForTesting
137     int mNumOfBroadcastReceiverStates = 0;
138     private BluetoothAdapter mBluetoothAdapter =
139             BluetoothAdapter.getDefaultAdapter();
140     private ServiceFactory mFactory = new ServiceFactory();
141     @VisibleForTesting
142     int mPendingOperation = -1;
143     @VisibleForTesting
144     byte mPendingSourceId = -1;
145     @VisibleForTesting
146     BluetoothLeBroadcastMetadata mPendingMetadata = null;
147     private BluetoothLeBroadcastReceiveState mSetBroadcastPINRcvState = null;
148     @VisibleForTesting
149     boolean mSetBroadcastCodePending = false;
150     private final Map<Integer, Boolean> mPendingRemove = new HashMap();
151     // Psync and PAST interfaces
152     private PeriodicAdvertisingManager mPeriodicAdvManager;
153     private boolean mAutoAssist = false;
154     @VisibleForTesting
155     boolean mAutoTriggered = false;
156     @VisibleForTesting
157     boolean mNoStopScanOffload = false;
158     private boolean mDefNoPAS = false;
159     private boolean mForceSB = false;
160     private int mBroadcastSourceIdLength = 3;
161     @VisibleForTesting
162     byte mNextSourceId = 0;
163     private boolean mAllowReconnect = false;
164     @VisibleForTesting
165     BluetoothGattTestableWrapper mBluetoothGatt = null;
166     BluetoothGattCallback mGattCallback = null;
167 
BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs)168     BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper,
169             int connectTimeoutMs) {
170         super(TAG + "(" + device.toString() + ")", looper);
171         mDevice = device;
172         mService = svc;
173         mConnectTimeoutMs = connectTimeoutMs;
174         addState(mDisconnected);
175         addState(mConnected);
176         addState(mConnecting);
177         addState(mConnectedProcessing);
178         setInitialState(mDisconnected);
179         // PSYNC and PAST instances
180         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
181         if (mBluetoothAdapter != null) {
182             mPeriodicAdvManager = mBluetoothAdapter.getPeriodicAdvertisingManager();
183         }
184         long token = Binder.clearCallingIdentity();
185         mIsAllowedList = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
186                 "persist.vendor.service.bt.wl", true);
187         mDefNoPAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
188                 "persist.vendor.service.bt.defNoPAS", false);
189         mForceSB = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
190                 "persist.vendor.service.bt.forceSB", false);
191         Binder.restoreCallingIdentity(token);
192     }
193 
make(BluetoothDevice device, BassClientService svc, Looper looper)194     static BassClientStateMachine make(BluetoothDevice device,
195             BassClientService svc, Looper looper) {
196         Log.d(TAG, "make for device " + device);
197         BassClientStateMachine BassclientSm = new BassClientStateMachine(device, svc, looper,
198                 BassConstants.CONNECT_TIMEOUT_MS);
199         BassclientSm.start();
200         return BassclientSm;
201     }
202 
destroy(BassClientStateMachine stateMachine)203     static void destroy(BassClientStateMachine stateMachine) {
204         Log.i(TAG, "destroy");
205         if (stateMachine == null) {
206             Log.w(TAG, "destroy(), stateMachine is null");
207             return;
208         }
209         stateMachine.doQuit();
210         stateMachine.cleanup();
211     }
212 
doQuit()213     public void doQuit() {
214         log("doQuit for device " + mDevice);
215         quitNow();
216     }
217 
cleanup()218     public void cleanup() {
219         log("cleanup for device " + mDevice);
220         clearCharsCache();
221 
222         if (mBluetoothGatt != null) {
223             log("disconnect gatt");
224             mBluetoothGatt.disconnect();
225             mBluetoothGatt.close();
226             mBluetoothGatt = null;
227             mGattCallback = null;
228         }
229         mPendingOperation = -1;
230         mPendingSourceId = -1;
231         mPendingMetadata = null;
232         mCurrentMetadata.clear();
233         mPendingRemove.clear();
234     }
235 
hasPendingSourceOperation()236     Boolean hasPendingSourceOperation() {
237         return mPendingMetadata != null;
238     }
239 
getCurrentBroadcastMetadata(Integer sourceId)240     BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
241         return mCurrentMetadata.getOrDefault(sourceId, null);
242     }
243 
setCurrentBroadcastMetadata(Integer sourceId, BluetoothLeBroadcastMetadata metadata)244     private void setCurrentBroadcastMetadata(Integer sourceId,
245             BluetoothLeBroadcastMetadata metadata) {
246         if (metadata != null) {
247             mCurrentMetadata.put(sourceId, metadata);
248         } else {
249             mCurrentMetadata.remove(sourceId);
250         }
251     }
252 
isPendingRemove(Integer sourceId)253     boolean isPendingRemove(Integer sourceId) {
254         return mPendingRemove.getOrDefault(sourceId, false);
255     }
256 
setPendingRemove(Integer sourceId, boolean remove)257     private void setPendingRemove(Integer sourceId, boolean remove) {
258         if (remove) {
259             mPendingRemove.put(sourceId, remove);
260         } else {
261             mPendingRemove.remove(sourceId);
262         }
263     }
264 
getBroadcastReceiveStateForSourceDevice( BluetoothDevice srcDevice)265     BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceDevice(
266             BluetoothDevice srcDevice) {
267         List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources();
268         BluetoothLeBroadcastReceiveState state = null;
269         for (int i = 0; i < currentSources.size(); i++) {
270             BluetoothDevice device = currentSources.get(i).getSourceDevice();
271             if (device != null && device.equals(srcDevice)) {
272                 state = currentSources.get(i);
273                 Log.e(TAG,
274                         "getBroadcastReceiveStateForSourceDevice: returns for: "
275                                 + srcDevice + "&srcInfo" + state);
276                 return state;
277             }
278         }
279         return null;
280     }
281 
getBroadcastReceiveStateForSourceId(int sourceId)282     BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceId(int sourceId) {
283         List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources();
284         for (int i = 0; i < currentSources.size(); i++) {
285             if (sourceId == currentSources.get(i).getSourceId()) {
286                 return currentSources.get(i);
287             }
288         }
289         return null;
290     }
291 
parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData)292     void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) {
293         log("parseBaseData" + Arrays.toString(serviceData));
294         BaseData base = BaseData.parseBaseData(serviceData);
295         if (base != null) {
296             mService.updateBase(syncHandle, base);
297             base.print();
298             if (mAutoTriggered) {
299                 // successful auto periodic synchrnization with source
300                 log("auto triggered assist");
301                 mAutoTriggered = false;
302                 // perform PAST with this device
303                 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
304                 if (srcDevice != null) {
305                     BluetoothLeBroadcastReceiveState recvState =
306                             getBroadcastReceiveStateForSourceDevice(srcDevice);
307                     processPASyncState(recvState);
308                 } else {
309                     Log.w(TAG, "Autoassist: no matching device");
310                 }
311             }
312         } else {
313             Log.e(TAG, "Seems BASE is not in parsable format");
314             if (!mAutoTriggered) {
315                 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
316                 cancelActiveSync(srcDevice);
317             } else {
318                 mAutoTriggered = false;
319             }
320         }
321     }
322 
parseScanRecord(int syncHandle, ScanRecord record)323     void parseScanRecord(int syncHandle, ScanRecord record) {
324         log("parseScanRecord" + record);
325         BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
326         Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData();
327         if (bmsAdvDataMap != null) {
328             for (Map.Entry<ParcelUuid, byte[]> entry : bmsAdvDataMap.entrySet()) {
329                 log("ParcelUUid = " + entry.getKey() + ", Value = "
330                         + Arrays.toString(entry.getValue()));
331             }
332         }
333         byte[] advData = record.getServiceData(BassConstants.BASIC_AUDIO_UUID);
334         if (advData != null) {
335             parseBaseData(mDevice, syncHandle, advData);
336         } else {
337             Log.e(TAG, "No service data in Scan record");
338             if (!mAutoTriggered) {
339                 cancelActiveSync(srcDevice);
340             } else {
341                 mAutoTriggered = false;
342             }
343         }
344     }
345 
selectSource( ScanResult scanRes, boolean autoTriggered)346     private boolean selectSource(
347             ScanResult scanRes, boolean autoTriggered) {
348         log("selectSource: ScanResult " + scanRes);
349         mAutoTriggered = autoTriggered;
350         mFirstTimeBisDiscovery = true;
351         mPASyncRetryCounter = 1;
352         // Cache Scan res for Retrys
353         mScanRes = scanRes;
354         /*This is an override case if Previous sync is still active, cancel It, but don't stop the
355          * Scan offload as we still trying to assist remote
356          */
357         mNoStopScanOffload = true;
358         cancelActiveSync(null);
359         try {
360             BluetoothMethodProxy.getInstance().periodicAdvertisingManagerRegisterSync(
361                     mPeriodicAdvManager, scanRes, 0, BassConstants.PSYNC_TIMEOUT,
362                     mPeriodicAdvCallback, null);
363         } catch (IllegalArgumentException ex) {
364             Log.w(TAG, "registerSync:IllegalArgumentException");
365             Message message = obtainMessage(STOP_SCAN_OFFLOAD);
366             sendMessage(message);
367             return false;
368         }
369         // updating mainly for Address type and PA Interval here
370         // extract BroadcastId from ScanResult
371         ScanRecord scanRecord = scanRes.getScanRecord();
372         if (scanRecord != null) {
373             Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData();
374             int broadcastId = BassConstants.INVALID_BROADCAST_ID;
375             if (listOfUuids != null) {
376                 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) {
377                     byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID);
378                     broadcastId = BassUtils.parseBroadcastId(bId);
379                 }
380             }
381             mService.updatePeriodicAdvertisementResultMap(
382                     scanRes.getDevice(),
383                     scanRes.getDevice().getAddressType(),
384                     BassConstants.INVALID_SYNC_HANDLE,
385                     BassConstants.INVALID_ADV_SID,
386                     scanRes.getPeriodicAdvertisingInterval(),
387                     broadcastId);
388         }
389         return true;
390     }
391 
cancelActiveSync(BluetoothDevice sourceDev)392     private void cancelActiveSync(BluetoothDevice sourceDev) {
393         log("cancelActiveSync");
394         BluetoothDevice activeSyncedSrc = mService.getActiveSyncedSource(mDevice);
395 
396         /* Stop sync if there is some running */
397         if (activeSyncedSrc != null && (sourceDev == null || activeSyncedSrc.equals(sourceDev))) {
398             removeMessages(PSYNC_ACTIVE_TIMEOUT);
399             try {
400                 log("calling unregisterSync");
401                 mPeriodicAdvManager.unregisterSync(mPeriodicAdvCallback);
402             } catch (IllegalArgumentException ex) {
403                 Log.w(TAG, "unregisterSync:IllegalArgumentException");
404             }
405             mService.clearPeriodicAdvertisementResult(activeSyncedSrc);
406             mService.setActiveSyncedSource(mDevice, null);
407             if (!mNoStopScanOffload) {
408                 // trigger scan stop here
409                 Message message = obtainMessage(STOP_SCAN_OFFLOAD);
410                 sendMessage(message);
411             }
412         }
413         mNoStopScanOffload = false;
414     }
415 
getBroadcastMetadataFromBaseData(BaseData baseData, BluetoothDevice device)416     private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData(BaseData baseData,
417             BluetoothDevice device) {
418         BluetoothLeBroadcastMetadata.Builder metaData =
419                 new BluetoothLeBroadcastMetadata.Builder();
420         int index = 0;
421         for (BaseData.BaseInformation baseLevel2 : baseData.getLevelTwo()) {
422             BluetoothLeBroadcastSubgroup.Builder subGroup =
423                     new BluetoothLeBroadcastSubgroup.Builder();
424             for (int j = 0; j < baseLevel2.numSubGroups; j ++) {
425                 BaseData.BaseInformation baseLevel3 =
426                         baseData.getLevelThree().get(index++);
427                 BluetoothLeBroadcastChannel.Builder channel =
428                         new BluetoothLeBroadcastChannel.Builder();
429                 channel.setChannelIndex(baseLevel3.index);
430                 channel.setCodecMetadata(BluetoothLeAudioCodecConfigMetadata.
431                         fromRawBytes(baseLevel3.codecConfigInfo));
432                 channel.setSelected(false);
433                 subGroup.addChannel(channel.build());
434             }
435             byte[] arrayCodecId = baseLevel2.codecId;
436             long codeId = (long) ((arrayCodecId[4] & 0xff) << 32
437                     | (arrayCodecId[3] & 0xff) << 24
438                     | (arrayCodecId[2] & 0xff) << 16
439                     | (arrayCodecId[1] & 0xff) << 8
440                     | (arrayCodecId[0] & 0xff));
441             subGroup.setCodecId(codeId);
442             subGroup.setCodecSpecificConfig(BluetoothLeAudioCodecConfigMetadata.
443                     fromRawBytes(baseLevel2.codecConfigInfo));
444             subGroup.setContentMetadata(BluetoothLeAudioContentMetadata.
445                     fromRawBytes(baseLevel2.metaData));
446             metaData.addSubgroup(subGroup.build());
447         }
448         metaData.setSourceDevice(device, device.getAddressType());
449         byte[] arrayPresentationDelay = baseData.getLevelOne().presentationDelay;
450         int presentationDelay = (int) ((arrayPresentationDelay[2] & 0xff) << 16
451                 | (arrayPresentationDelay[1] & 0xff)
452                 | (arrayPresentationDelay[0] & 0xff));
453         metaData.setPresentationDelayMicros(presentationDelay);
454         PeriodicAdvertisementResult result =
455                 mService.getPeriodicAdvertisementResult(device);
456         if (result != null) {
457             int broadcastId = result.getBroadcastId();
458             log("broadcast ID: " + broadcastId);
459             metaData.setBroadcastId(broadcastId);
460             metaData.setSourceAdvertisingSid(result.getAdvSid());
461         }
462         return metaData.build();
463     }
464 
465     /** Internal periodc Advertising manager callback */
466     private PeriodicAdvertisingCallback mPeriodicAdvCallback =
467             new PeriodicAdvertisingCallback() {
468                 @Override
469                 public void onSyncEstablished(
470                         int syncHandle,
471                         BluetoothDevice device,
472                         int advertisingSid,
473                         int skip,
474                         int timeout,
475                         int status) {
476                     log("onSyncEstablished syncHandle: " + syncHandle
477                             + ", device: " + device
478                             + ", advertisingSid: " + advertisingSid
479                             + ", skip: " + skip
480                             + ", timeout: " + timeout
481                             + ", status: " + status);
482                     if (status == BluetoothGatt.GATT_SUCCESS) {
483                         // updates syncHandle, advSid
484                         mService.updatePeriodicAdvertisementResultMap(
485                                 device,
486                                 BassConstants.INVALID_ADV_ADDRESS_TYPE,
487                                 syncHandle,
488                                 advertisingSid,
489                                 BassConstants.INVALID_ADV_INTERVAL,
490                                 BassConstants.INVALID_BROADCAST_ID);
491                         sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT,
492                                 BassConstants.PSYNC_ACTIVE_TIMEOUT_MS);
493                         mService.setActiveSyncedSource(mDevice, device);
494                     } else {
495                         log("failed to sync to PA: " + mPASyncRetryCounter);
496                         mScanRes = null;
497                         if (!mAutoTriggered) {
498                             Message message = obtainMessage(STOP_SCAN_OFFLOAD);
499                             sendMessage(message);
500                         }
501                         mAutoTriggered = false;
502                     }
503                 }
504 
505                 @Override
506                 public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
507                     log("onPeriodicAdvertisingReport");
508                     // Parse the BIS indices from report's service data
509                     if (mFirstTimeBisDiscovery) {
510                         parseScanRecord(report.getSyncHandle(), report.getData());
511                         BaseData baseData = mService.getBase(report.getSyncHandle());
512                         if (baseData != null) {
513                             BluetoothLeBroadcastMetadata metaData =
514                                     getBroadcastMetadataFromBaseData(baseData,
515                                             mService.getDeviceForSyncHandle(report.getSyncHandle()));
516                             mService.getCallbacks().notifySourceFound(metaData);
517                         }
518                         mFirstTimeBisDiscovery = false;
519                     }
520                 }
521 
522                 @Override
523                 public void onSyncLost(int syncHandle) {
524                     log("OnSyncLost" + syncHandle);
525                     BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle);
526                     cancelActiveSync(srcDevice);
527                 }
528             };
529 
broadcastReceiverState( BluetoothLeBroadcastReceiveState state, int sourceId)530     private void broadcastReceiverState(
531             BluetoothLeBroadcastReceiveState state, int sourceId) {
532         log("broadcastReceiverState: " + mDevice);
533         mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state);
534     }
535 
536     @VisibleForTesting
isEmpty(final byte[] data)537     static boolean isEmpty(final byte[] data) {
538         return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0);
539     }
540 
processPASyncState(BluetoothLeBroadcastReceiveState recvState)541     private void processPASyncState(BluetoothLeBroadcastReceiveState recvState) {
542         log("processPASyncState " + recvState);
543         int serviceData = 0;
544         if (recvState == null) {
545             Log.e(TAG, "processPASyncState: recvState is null");
546             return;
547         }
548         int state = recvState.getPaSyncState();
549         if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) {
550             log("Initiate PAST procedure");
551             PeriodicAdvertisementResult result =
552                     mService.getPeriodicAdvertisementResult(
553                     recvState.getSourceDevice());
554             if (result != null) {
555                 int syncHandle = result.getSyncHandle();
556                 log("processPASyncState: syncHandle " + result.getSyncHandle());
557                 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) {
558                     serviceData = 0x000000FF & recvState.getSourceId();
559                     serviceData = serviceData << 8;
560                     //advA matches EXT_ADV_ADDRESS
561                     //also matches source address (as we would have written)
562                     serviceData = serviceData
563                             & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
564                     serviceData = serviceData
565                             & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
566                     log("Initiate PAST for: " + mDevice + ", syncHandle: " +  syncHandle
567                             + "serviceData" + serviceData);
568                     BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSync(
569                             mPeriodicAdvManager, mDevice, serviceData, syncHandle);
570                 }
571             } else {
572                 if (mService.isLocalBroadcast(mPendingMetadata)) {
573                     int advHandle = mPendingMetadata.getSourceAdvertisingSid();
574                     serviceData = 0x000000FF & recvState.getSourceId();
575                     serviceData = serviceData << 8;
576                     // Address we set in the Source Address can differ from the address in the air
577                     serviceData = serviceData
578                             | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS;
579                     log("Initiate local broadcast PAST for: " + mDevice
580                             + ", advSID/Handle: " +  advHandle
581                             + ", serviceData: " + serviceData);
582                     BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSetInfo(
583                             mPeriodicAdvManager, mDevice, serviceData, advHandle,
584                             mPeriodicAdvCallback);
585                 } else {
586                     Log.e(TAG, "There is no valid sync handle for this Source");
587                     if (mAutoAssist) {
588                         // Initiate Auto Assist procedure for this device
589                         mService.getBassUtils().triggerAutoAssist(recvState);
590                     }
591                 }
592             }
593         } else if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
594                 || state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_NO_PAST) {
595             Message message = obtainMessage(STOP_SCAN_OFFLOAD);
596             sendMessage(message);
597         }
598     }
599 
checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState)600     private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) {
601         log("checkAndUpdateBroadcastCode");
602         // non colocated case, Broadcast PIN should have been updated from lyaer
603         // If there is pending one process it Now
604         if (recvState.getBigEncryptionState()
605                 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED
606                 && mSetBroadcastCodePending) {
607             log("Update the Broadcast now");
608             Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE);
609 
610             /* Use cached receiver state if previousely didn't finished setting broadcast code or
611              * use current receiver state if this is a first check and update
612              */
613             if (mSetBroadcastPINRcvState != null) {
614                 m.obj = mSetBroadcastPINRcvState;
615             } else {
616                 m.obj = recvState;
617             }
618 
619             sendMessage(m);
620         }
621     }
622 
parseBroadcastReceiverState( byte[] receiverState)623     private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(
624             byte[] receiverState) {
625         byte sourceId = 0;
626         if (receiverState.length > 0) {
627             sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX];
628         }
629         log("processBroadcastReceiverState: receiverState length: " + receiverState.length);
630 
631         BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
632         BluetoothLeBroadcastReceiveState recvState = null;
633         if (receiverState.length == 0
634                 || isEmpty(Arrays.copyOfRange(receiverState, 1, receiverState.length - 1))) {
635             String emptyBluetoothDevice = "00:00:00:00:00:00";
636             if (mPendingOperation == REMOVE_BCAST_SOURCE) {
637                 recvState = new BluetoothLeBroadcastReceiveState(mPendingSourceId,
638                         BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType
639                         btAdapter.getRemoteDevice(emptyBluetoothDevice),  // sourceDevice
640                         0,  // sourceAdvertisingSid
641                         0,  // broadcastId
642                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState
643                         // bigEncryptionState
644                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
645                         null,   // badCode
646                         0,  // numSubgroups
647                         Arrays.asList(new Long[0]),   // bisSyncState
648                         Arrays.asList(new BluetoothLeAudioContentMetadata[0])    // subgroupMetadata
649                 );
650             } else if (receiverState.length == 0) {
651                 if (mBluetoothLeBroadcastReceiveStates != null) {
652                     mNextSourceId = (byte) mBluetoothLeBroadcastReceiveStates.size();
653                 }
654                 if (mNextSourceId >= mNumOfBroadcastReceiverStates) {
655                     Log.e(TAG, "reached the remote supported max SourceInfos");
656                     return null;
657                 }
658                 mNextSourceId++;
659                 recvState = new BluetoothLeBroadcastReceiveState(mNextSourceId,
660                         BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType
661                         btAdapter.getRemoteDevice(emptyBluetoothDevice),   // sourceDevice
662                         0,  // sourceAdvertisingSid
663                         0,  // broadcastId
664                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState
665                         // bigEncryptionState
666                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
667                         null,   // badCode
668                         0,  // numSubgroups
669                         Arrays.asList(new Long[0]),   // bisSyncState
670                         Arrays.asList(new BluetoothLeAudioContentMetadata[0])    // subgroupMetadata
671                 );
672             }
673         } else {
674             byte metaDataSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX];
675             byte encryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX];
676             byte[] badBroadcastCode = null;
677             int badBroadcastCodeLen = 0;
678             if (encryptionStatus
679                     == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) {
680                 badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE];
681                 System.arraycopy(
682                         receiverState,
683                         BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX,
684                         badBroadcastCode,
685                         0,
686                         BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE);
687                 badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE;
688             }
689             byte numSubGroups = receiverState[BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX
690                     + badBroadcastCodeLen];
691             int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX
692                     + badBroadcastCodeLen + 1;
693             ArrayList<BluetoothLeAudioContentMetadata> metadataList =
694                     new ArrayList<BluetoothLeAudioContentMetadata>();
695             ArrayList<Long> audioSyncState = new ArrayList<Long>();
696             for (int i = 0; i < numSubGroups; i++) {
697                 byte[] audioSyncIndex = new byte[BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE];
698                 System.arraycopy(receiverState, offset, audioSyncIndex, 0,
699                         BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE);
700                 offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE;
701                 audioSyncState.add((long) Utils.byteArrayToInt(audioSyncIndex));
702 
703                 byte metaDataLength = receiverState[offset++];
704                 if (metaDataLength > 0) {
705                     log("metadata of length: " + metaDataLength + "is available");
706                     byte[] metaData = new byte[metaDataLength];
707                     System.arraycopy(receiverState, offset, metaData, 0, metaDataLength);
708                     offset += metaDataLength;
709                     metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData));
710                 } else {
711                     metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0]));
712                 }
713             }
714             byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength];
715             System.arraycopy(
716                     receiverState,
717                     BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX,
718                     broadcastIdBytes,
719                     0,
720                     mBroadcastSourceIdLength);
721             int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes);
722             byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE];
723             System.arraycopy(
724                     receiverState,
725                     BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX,
726                     sourceAddress,
727                     0,
728                     BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE);
729             byte sourceAddressType = receiverState[BassConstants
730                     .BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX];
731             BassUtils.reverse(sourceAddress);
732             String address = Utils.getAddressStringFromByte(sourceAddress);
733             BluetoothDevice device = btAdapter.getRemoteLeDevice(
734                     address, sourceAddressType);
735             byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX];
736             recvState = new BluetoothLeBroadcastReceiveState(
737                     sourceId,
738                     (int) sourceAddressType,
739                     device,
740                     sourceAdvSid,
741                     broadcastId,
742                     (int) metaDataSyncState,
743                     (int) encryptionStatus,
744                     badBroadcastCode,
745                     numSubGroups,
746                     audioSyncState,
747                     metadataList);
748             log("Receiver state: "
749                     + "\n\tSource ID: " + sourceId
750                     + "\n\tSource Address Type: " + (int) sourceAddressType
751                     + "\n\tDevice: " + device
752                     + "\n\tSource Adv SID: " + sourceAdvSid
753                     + "\n\tBroadcast ID: " + broadcastId
754                     + "\n\tMetadata Sync State: " + (int) metaDataSyncState
755                     + "\n\tEncryption Status: " + (int) encryptionStatus
756                     + "\n\tBad Broadcast Code: " + Arrays.toString(badBroadcastCode)
757                     + "\n\tNumber Of Subgroups: " + numSubGroups
758                     + "\n\tAudio Sync State: " + audioSyncState
759                     + "\n\tMetadata: " + metadataList);
760         }
761         return recvState;
762     }
763 
processBroadcastReceiverState( byte[] receiverState, BluetoothGattCharacteristic characteristic)764     private void processBroadcastReceiverState(
765             byte[] receiverState, BluetoothGattCharacteristic characteristic) {
766         log("processBroadcastReceiverState: characteristic:" + characteristic);
767         BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState(
768                 receiverState);
769         if (recvState == null) {
770             log("processBroadcastReceiverState: Null recvState");
771             return;
772         } else if (recvState.getSourceId() == -1) {
773             log("processBroadcastReceiverState: invalid index: " + recvState.getSourceId());
774             return;
775         }
776         BluetoothLeBroadcastReceiveState oldRecvState =
777                 mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId());
778         if (oldRecvState == null) {
779             log("Initial Read and Populating values");
780             if (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates) {
781                 Log.e(TAG, "reached the Max SourceInfos");
782                 return;
783             }
784             mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState);
785             checkAndUpdateBroadcastCode(recvState);
786             processPASyncState(recvState);
787         } else {
788             log("old sourceInfo: " + oldRecvState);
789             log("new sourceInfo: " + recvState);
790             mBluetoothLeBroadcastReceiveStates.replace(characteristic.getInstanceId(), recvState);
791             String emptyBluetoothDevice = "00:00:00:00:00:00";
792             if (oldRecvState.getSourceDevice() == null
793                     || oldRecvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) {
794                 log("New Source Addition");
795                 mService.getCallbacks().notifySourceAdded(mDevice, recvState,
796                         BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
797                 if (mPendingMetadata != null) {
798                     setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata);
799                 }
800                 checkAndUpdateBroadcastCode(recvState);
801                 processPASyncState(recvState);
802             } else {
803                 if (recvState.getSourceDevice() == null
804                         || recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) {
805                     BluetoothDevice removedDevice = oldRecvState.getSourceDevice();
806                     log("sourceInfo removal" + removedDevice);
807                     cancelActiveSync(removedDevice);
808                     setCurrentBroadcastMetadata(oldRecvState.getSourceId(), null);
809                     mService.getCallbacks().notifySourceRemoved(mDevice,
810                             oldRecvState.getSourceId(),
811                             BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
812                 } else {
813                     log("update to an existing recvState");
814                     setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata);
815                     mService.getCallbacks().notifySourceModified(mDevice,
816                             recvState.getSourceId(), BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
817                     checkAndUpdateBroadcastCode(recvState);
818                     processPASyncState(recvState);
819 
820                     if (isPendingRemove(recvState.getSourceId())) {
821                         Message message = obtainMessage(REMOVE_BCAST_SOURCE);
822                         message.arg1 = recvState.getSourceId();
823                         sendMessage(message);
824                     }
825                 }
826             }
827         }
828         broadcastReceiverState(recvState, recvState.getSourceId());
829     }
830 
831     // Implements callback methods for GATT events that the app cares about.
832     // For example, connection change and services discovered.
833     final class GattCallback extends BluetoothGattCallback {
834         @Override
onConnectionStateChange(BluetoothGatt gatt, int status, int newState)835         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
836             boolean isStateChanged = false;
837             log("onConnectionStateChange : Status=" + status + "newState" + newState);
838             if (newState == BluetoothProfile.STATE_CONNECTED
839                     && getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
840                 isStateChanged = true;
841                 Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice);
842                 if (mService.okToConnect(mDevice)) {
843                     log("Bassclient Connected to: " + mDevice);
844                     if (mBluetoothGatt != null) {
845                         log("Attempting to start service discovery:"
846                                 + mBluetoothGatt.discoverServices());
847                         mDiscoveryInitiated = true;
848                     }
849                 } else if (mBluetoothGatt != null) {
850                     // Reject the connection
851                     Log.w(TAG, "Bassclient Connect request rejected: " + mDevice);
852                     mBluetoothGatt.disconnect();
853                     mBluetoothGatt.close();
854                     mBluetoothGatt = null;
855                     // force move to disconnected
856                     newState = BluetoothProfile.STATE_DISCONNECTED;
857                 }
858             } else if (newState == BluetoothProfile.STATE_DISCONNECTED
859                     && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
860                 isStateChanged = true;
861                 log("Disconnected from Bass GATT server.");
862             }
863             if (isStateChanged) {
864                 Message m = obtainMessage(CONNECTION_STATE_CHANGED);
865                 m.obj = newState;
866                 sendMessage(m);
867             }
868         }
869 
870         @Override
onServicesDiscovered(BluetoothGatt gatt, int status)871         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
872             log("onServicesDiscovered:" + status);
873             if (mDiscoveryInitiated) {
874                 mDiscoveryInitiated = false;
875                 if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) {
876                     mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES);
877                     mMTUChangeRequested = true;
878                 } else {
879                     Log.w(TAG, "onServicesDiscovered received: "
880                             + status + "mBluetoothGatt" + mBluetoothGatt);
881                 }
882             } else {
883                 log("remote initiated callback");
884             }
885         }
886 
887         @Override
onCharacteristicRead( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)888         public void onCharacteristicRead(
889                 BluetoothGatt gatt,
890                 BluetoothGattCharacteristic characteristic,
891                 int status) {
892             log("onCharacteristicRead:: status: " + status + "char:" + characteristic);
893             if (status == BluetoothGatt.GATT_SUCCESS && characteristic.getUuid()
894                     .equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
895                 log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status);
896                 if (characteristic.getValue() == null) {
897                     Log.e(TAG, "Remote receiver state is NULL");
898                     return;
899                 }
900                 logByteArray("Received ", characteristic.getValue(), 0,
901                         characteristic.getValue().length);
902                 processBroadcastReceiverState(characteristic.getValue(), characteristic);
903             }
904             // switch to receiving notifications after initial characteristic read
905             BluetoothGattDescriptor desc = characteristic
906                     .getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG);
907             if (mBluetoothGatt != null && desc != null) {
908                 log("Setting the value for Desc");
909                 mBluetoothGatt.setCharacteristicNotification(characteristic, true);
910                 desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
911                 mBluetoothGatt.writeDescriptor(desc);
912             } else {
913                 Log.w(TAG, "CCC for " + characteristic + "seem to be not present");
914                 // at least move the SM to stable state
915                 Message m = obtainMessage(GATT_TXN_PROCESSED);
916                 m.arg1 = status;
917                 sendMessage(m);
918             }
919         }
920 
921         @Override
onDescriptorWrite( BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)922         public void onDescriptorWrite(
923                 BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
924             log("onDescriptorWrite");
925             if (status == BluetoothGatt.GATT_SUCCESS
926                     && descriptor.getUuid()
927                     .equals(BassConstants.CLIENT_CHARACTERISTIC_CONFIG)) {
928                 log("CCC write resp");
929             }
930 
931             // Move the SM to connected so further reads happens
932             Message m = obtainMessage(GATT_TXN_PROCESSED);
933             m.arg1 = status;
934             sendMessage(m);
935         }
936 
937         @Override
onMtuChanged(BluetoothGatt gatt, int mtu, int status)938         public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
939             log("onMtuChanged: mtu:" + mtu);
940             if (mMTUChangeRequested && mBluetoothGatt != null) {
941                 acquireAllBassChars();
942                 mMTUChangeRequested = false;
943             } else {
944                 log("onMtuChanged is remote initiated trigger, mBluetoothGatt:"
945                         + mBluetoothGatt);
946             }
947         }
948 
949         @Override
onCharacteristicChanged( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)950         public void onCharacteristicChanged(
951                 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
952             log("onCharacteristicChanged :: " + characteristic.getUuid().toString());
953             if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) {
954                 log("onCharacteristicChanged is rcvr State :: "
955                         + characteristic.getUuid().toString());
956                 if (characteristic.getValue() == null) {
957                     Log.e(TAG, "Remote receiver state is NULL");
958                     return;
959                 }
960                 logByteArray("onCharacteristicChanged: Received ",
961                         characteristic.getValue(),
962                         0,
963                         characteristic.getValue().length);
964                 processBroadcastReceiverState(characteristic.getValue(), characteristic);
965             }
966         }
967 
968         @Override
onCharacteristicWrite( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)969         public void onCharacteristicWrite(
970                 BluetoothGatt gatt,
971                 BluetoothGattCharacteristic characteristic,
972                 int status) {
973             log("onCharacteristicWrite: " + characteristic.getUuid().toString()
974                     + "status:" + status);
975             if (status == 0
976                     && characteristic.getUuid()
977                     .equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
978                 log("BASS_BCAST_AUDIO_SCAN_CTRL_POINT is written successfully");
979             }
980             Message m = obtainMessage(GATT_TXN_PROCESSED);
981             m.arg1 = status;
982             sendMessage(m);
983         }
984     }
985 
986     /**
987      * Connects to the GATT server of the device.
988      *
989      * @return {@code true} if it successfully connects to the GATT server.
990      */
991     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
connectGatt(Boolean autoConnect)992     public boolean connectGatt(Boolean autoConnect) {
993         if (mGattCallback == null) {
994             mGattCallback = new GattCallback();
995         }
996 
997         BluetoothGatt gatt = mDevice.connectGatt(mService, autoConnect,
998                 mGattCallback, BluetoothDevice.TRANSPORT_LE,
999                 (BluetoothDevice.PHY_LE_1M_MASK
1000                         | BluetoothDevice.PHY_LE_2M_MASK
1001                         | BluetoothDevice.PHY_LE_CODED_MASK), null);
1002 
1003         if (gatt != null) {
1004             mBluetoothGatt = new BluetoothGattTestableWrapper(gatt);
1005         }
1006 
1007         return mBluetoothGatt != null;
1008     }
1009 
1010     /**
1011      * getAllSources
1012      */
getAllSources()1013     public List<BluetoothLeBroadcastReceiveState> getAllSources() {
1014         log("getAllSources");
1015         List list = new ArrayList(mBluetoothLeBroadcastReceiveStates.values());
1016         return list;
1017     }
1018 
acquireAllBassChars()1019     void acquireAllBassChars() {
1020         clearCharsCache();
1021         BluetoothGattService service = null;
1022         if (mBluetoothGatt != null) {
1023             log("getting Bass Service handle");
1024             service = mBluetoothGatt.getService(BassConstants.BASS_UUID);
1025         }
1026         if (service == null) {
1027             log("acquireAllBassChars: BASS service not found");
1028             return;
1029         }
1030         log("found BASS_SERVICE");
1031         List<BluetoothGattCharacteristic> allChars = service.getCharacteristics();
1032         int numOfChars = allChars.size();
1033         mNumOfBroadcastReceiverStates = numOfChars - 1;
1034         log("Total number of chars" + numOfChars);
1035         for (int i = 0; i < allChars.size(); i++) {
1036             if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) {
1037                 mBroadcastScanControlPoint = allChars.get(i);
1038                 log("Index of ScanCtrlPoint:" + i);
1039             } else {
1040                 log("Reading " + i + "th ReceiverState");
1041                 mBroadcastCharacteristics.add(allChars.get(i));
1042                 Message m = obtainMessage(READ_BASS_CHARACTERISTICS);
1043                 m.obj = allChars.get(i);
1044                 sendMessage(m);
1045             }
1046         }
1047     }
1048 
clearCharsCache()1049     void clearCharsCache() {
1050         if (mBroadcastCharacteristics != null) {
1051             mBroadcastCharacteristics.clear();
1052         }
1053         if (mBroadcastScanControlPoint != null) {
1054             mBroadcastScanControlPoint = null;
1055         }
1056         mNumOfBroadcastReceiverStates = 0;
1057         if (mBluetoothLeBroadcastReceiveStates != null) {
1058             mBluetoothLeBroadcastReceiveStates.clear();
1059         }
1060         mPendingOperation = -1;
1061         mPendingMetadata = null;
1062         mCurrentMetadata.clear();
1063         mPendingRemove.clear();
1064     }
1065 
1066     @VisibleForTesting
1067     class Disconnected extends State {
1068         @Override
enter()1069         public void enter() {
1070             log("Enter Disconnected(" + mDevice + "): "
1071                     + messageWhatToString(getCurrentMessage().what));
1072             clearCharsCache();
1073             mNextSourceId = 0;
1074             removeDeferredMessages(DISCONNECT);
1075             if (mLastConnectionState == -1) {
1076                 log("no Broadcast of initial profile state ");
1077             } else {
1078                 broadcastConnectionState(
1079                         mDevice, mLastConnectionState, BluetoothProfile.STATE_DISCONNECTED);
1080                 if (mLastConnectionState != BluetoothProfile.STATE_DISCONNECTED) {
1081                     // Reconnect in background if not disallowed by the service
1082                     if (mService.okToConnect(mDevice) && mAllowReconnect) {
1083                         connectGatt(false);
1084                     }
1085                 }
1086             }
1087         }
1088 
1089         @Override
exit()1090         public void exit() {
1091             log("Exit Disconnected(" + mDevice + "): "
1092                     + messageWhatToString(getCurrentMessage().what));
1093             mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
1094         }
1095 
1096         @Override
processMessage(Message message)1097         public boolean processMessage(Message message) {
1098             log("Disconnected process message(" + mDevice
1099                     + "): " + messageWhatToString(message.what));
1100             switch (message.what) {
1101                 case CONNECT:
1102                     log("Connecting to " + mDevice);
1103                     if (mBluetoothGatt != null) {
1104                         Log.d(TAG, "clear off, pending wl connection");
1105                         mBluetoothGatt.disconnect();
1106                         mBluetoothGatt.close();
1107                         mBluetoothGatt = null;
1108                     }
1109                     mAllowReconnect = true;
1110                     if (connectGatt(mIsAllowedList)) {
1111                         transitionTo(mConnecting);
1112                     } else {
1113                         Log.e(TAG, "Disconnected: error connecting to " + mDevice);
1114                     }
1115                     break;
1116                 case DISCONNECT:
1117                     // Disconnect if there's an ongoing background connection
1118                     mAllowReconnect = false;
1119                     if (mBluetoothGatt != null) {
1120                         log("Cancelling the background connection to " + mDevice);
1121                         mBluetoothGatt.disconnect();
1122                         mBluetoothGatt.close();
1123                         mBluetoothGatt = null;
1124                     } else {
1125                         Log.d(TAG, "Disconnected: DISCONNECT ignored: " + mDevice);
1126                     }
1127                     break;
1128                 case CONNECTION_STATE_CHANGED:
1129                     int state = (int) message.obj;
1130                     Log.w(TAG, "connection state changed:" + state);
1131                     if (state == BluetoothProfile.STATE_CONNECTED) {
1132                         log("remote/wl connection");
1133                         transitionTo(mConnected);
1134                     } else {
1135                         Log.w(TAG, "Disconnected: Connection failed to " + mDevice);
1136                     }
1137                     break;
1138                 case PSYNC_ACTIVE_TIMEOUT:
1139                     cancelActiveSync(null);
1140                     break;
1141                 default:
1142                     log("DISCONNECTED: not handled message:" + message.what);
1143                     return NOT_HANDLED;
1144             }
1145             return HANDLED;
1146         }
1147     }
1148 
1149     @VisibleForTesting
1150     class Connecting extends State {
1151         @Override
enter()1152         public void enter() {
1153             log("Enter Connecting(" + mDevice + "): "
1154                     + messageWhatToString(getCurrentMessage().what));
1155             sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs);
1156             broadcastConnectionState(
1157                     mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTING);
1158         }
1159 
1160         @Override
exit()1161         public void exit() {
1162             log("Exit Connecting(" + mDevice + "): "
1163                     + messageWhatToString(getCurrentMessage().what));
1164             mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
1165             removeMessages(CONNECT_TIMEOUT);
1166         }
1167 
1168         @Override
processMessage(Message message)1169         public boolean processMessage(Message message) {
1170             log("Connecting process message(" + mDevice + "): "
1171                     + messageWhatToString(message.what));
1172             switch (message.what) {
1173                 case CONNECT:
1174                     log("Already Connecting to " + mDevice);
1175                     log("Ignore this connection request " + mDevice);
1176                     break;
1177                 case DISCONNECT:
1178                     Log.w(TAG, "Connecting: DISCONNECT deferred: " + mDevice);
1179                     deferMessage(message);
1180                     break;
1181                 case READ_BASS_CHARACTERISTICS:
1182                     Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
1183                     deferMessage(message);
1184                     break;
1185                 case CONNECTION_STATE_CHANGED:
1186                     int state = (int) message.obj;
1187                     Log.w(TAG, "Connecting: connection state changed:" + state);
1188                     if (state == BluetoothProfile.STATE_CONNECTED) {
1189                         transitionTo(mConnected);
1190                     } else {
1191                         Log.w(TAG, "Connection failed to " + mDevice);
1192                         transitionTo(mDisconnected);
1193                     }
1194                     break;
1195                 case CONNECT_TIMEOUT:
1196                     Log.w(TAG, "CONNECT_TIMEOUT");
1197                     BluetoothDevice device = (BluetoothDevice) message.obj;
1198                     if (!mDevice.equals(device)) {
1199                         Log.e(TAG, "Unknown device timeout " + device);
1200                         break;
1201                     }
1202                     transitionTo(mDisconnected);
1203                     break;
1204                 case PSYNC_ACTIVE_TIMEOUT:
1205                     deferMessage(message);
1206                     break;
1207                 default:
1208                     log("CONNECTING: not handled message:" + message.what);
1209                     return NOT_HANDLED;
1210             }
1211             return HANDLED;
1212         }
1213     }
1214 
getBisSyncFromChannelPreference( List<BluetoothLeBroadcastChannel> channels)1215     private static int getBisSyncFromChannelPreference(
1216                 List<BluetoothLeBroadcastChannel> channels) {
1217         int bisSync = 0;
1218         for (BluetoothLeBroadcastChannel channel : channels) {
1219             if (channel.isSelected()) {
1220                 if (channel.getChannelIndex() == 0) {
1221                     Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0");
1222                     continue;
1223                 }
1224                 bisSync |= 1 << (channel.getChannelIndex() - 1);
1225             }
1226         }
1227 
1228         return bisSync;
1229     }
1230 
convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData)1231     private byte[] convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData) {
1232         ByteArrayOutputStream stream = new ByteArrayOutputStream();
1233         BluetoothDevice advSource = metaData.getSourceDevice();
1234 
1235         // Opcode
1236         stream.write(OPCODE_ADD_SOURCE);
1237 
1238         // Advertiser_Address_Type
1239         stream.write(metaData.getSourceAddressType());
1240 
1241         // Advertiser_Address
1242         byte[] bcastSourceAddr = Utils.getBytesFromAddress(advSource.getAddress());
1243         BassUtils.reverse(bcastSourceAddr);
1244         stream.write(bcastSourceAddr, 0, 6);
1245         log("Address bytes: " + advSource.getAddress());
1246 
1247         // Advertising_SID
1248         stream.write(metaData.getSourceAdvertisingSid());
1249 
1250         // Broadcast_ID
1251         stream.write(metaData.getBroadcastId() & 0x00000000000000FF);
1252         stream.write((metaData.getBroadcastId() & 0x000000000000FF00) >>> 8);
1253         stream.write((metaData.getBroadcastId() & 0x0000000000FF0000) >>> 16);
1254         log("mBroadcastId: " + metaData.getBroadcastId());
1255 
1256         // PA_Sync
1257         if (!mDefNoPAS) {
1258             stream.write(0x01);
1259         } else {
1260             log("setting PA sync to ZERO");
1261             stream.write(0x00);
1262         }
1263 
1264         // PA_Interval
1265         stream.write((metaData.getPaSyncInterval() & 0x00000000000000FF));
1266         stream.write((metaData.getPaSyncInterval() & 0x000000000000FF00) >>> 8);
1267 
1268         // Num_Subgroups
1269         List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups();
1270         stream.write(metaData.getSubgroups().size());
1271 
1272         for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
1273             // BIS_Sync
1274             int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels());
1275             if (bisSync == 0) {
1276                 bisSync = 0xFFFFFFFF;
1277             }
1278             stream.write(bisSync & 0x00000000000000FF);
1279             stream.write((bisSync & 0x000000000000FF00) >>> 8);
1280             stream.write((bisSync & 0x0000000000FF0000) >>> 16);
1281             stream.write((bisSync & 0x00000000FF000000) >>> 24);
1282 
1283             // Metadata_Length
1284             BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata();
1285             stream.write(metadata.getRawMetadata().length);
1286 
1287             // Metadata
1288             stream.write(metadata.getRawMetadata(), 0, metadata.getRawMetadata().length);
1289         }
1290 
1291         byte[] res = stream.toByteArray();
1292         log("ADD_BCAST_SOURCE in Bytes");
1293         BassUtils.printByteArray(res);
1294         return res;
1295     }
1296 
convertBroadcastMetadataToUpdateSourceByteArray(int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync)1297     private byte[] convertBroadcastMetadataToUpdateSourceByteArray(int sourceId,
1298             BluetoothLeBroadcastMetadata metaData, int paSync) {
1299         BluetoothLeBroadcastReceiveState existingState =
1300                 getBroadcastReceiveStateForSourceId(sourceId);
1301         if (existingState == null) {
1302             log("no existing SI for update source op");
1303             return null;
1304         }
1305         BluetoothDevice broadcastSource = metaData.getSourceDevice();
1306         PeriodicAdvertisementResult paRes =
1307                 mService.getPeriodicAdvertisementResult(broadcastSource);
1308         if (paRes == null) {
1309             Log.e(TAG, "No matching psync, scan res for update");
1310             mService.getCallbacks().notifySourceRemoveFailed(
1311                     mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
1312             return null;
1313         }
1314         // populate metadata from BASE levelOne
1315         BaseData base = mService.getBase(paRes.getSyncHandle());
1316         if (base == null) {
1317             Log.e(TAG, "No valid base data populated for this device");
1318             mService.getCallbacks().notifySourceRemoveFailed(
1319                     mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
1320             return null;
1321         }
1322         byte numSubGroups = base.getNumberOfSubgroupsofBIG();
1323         byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
1324         int offset = 0;
1325         // Opcode
1326         res[offset++] = OPCODE_UPDATE_SOURCE;
1327         // Source_ID
1328         res[offset++] = (byte) sourceId;
1329         // PA_Sync
1330         if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) {
1331             res[offset++] = (byte) paSync;
1332         } else if (existingState.getPaSyncState()
1333                 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
1334             res[offset++] = (byte) (0x01);
1335         } else {
1336             res[offset++] = (byte) 0x00;
1337         }
1338         // PA_Interval
1339         res[offset++] = (byte) 0xFF;
1340         res[offset++] = (byte) 0xFF;
1341         // Num_Subgroups
1342         res[offset++] = numSubGroups;
1343         for (int i = 0; i < numSubGroups; i++) {
1344             int bisIndexValue;
1345             if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) {
1346                 bisIndexValue = 0;
1347             } else {
1348                 bisIndexValue = existingState.getBisSyncState().get(i).intValue();
1349             }
1350             log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
1351             // BIS_Sync
1352             res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF);
1353             res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8);
1354             res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16);
1355             res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24);
1356             // Metadata_Length; On Modify source, don't update any Metadata
1357             res[offset++] = 0;
1358         }
1359         log("UPDATE_BCAST_SOURCE in Bytes");
1360         BassUtils.printByteArray(res);
1361         return res;
1362     }
1363 
convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState)1364     private byte[] convertRecvStateToSetBroadcastCodeByteArray(
1365             BluetoothLeBroadcastReceiveState recvState) {
1366         byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN];
1367         // Opcode
1368         res[0] = OPCODE_SET_BCAST_PIN;
1369         // Source_ID
1370         res[1] = (byte) recvState.getSourceId();
1371         log("convertRecvStateToSetBroadcastCodeByteArray: Source device : "
1372                 + recvState.getSourceDevice());
1373         BluetoothLeBroadcastMetadata metaData =
1374                 getCurrentBroadcastMetadata(recvState.getSourceId());
1375         if (metaData == null) {
1376             Log.e(TAG, "Fail to find broadcast source, sourceId = "
1377                     + recvState.getSourceId());
1378             return null;
1379         }
1380         // Broadcast Code
1381         byte[] actualPIN = metaData.getBroadcastCode();
1382         if (actualPIN == null) {
1383             Log.e(TAG, "actual PIN is null");
1384             return null;
1385         } else {
1386             log("byte array broadcast Code:" + Arrays.toString(actualPIN));
1387             log("pinLength:" + actualPIN.length);
1388             // Broadcast_Code, Fill the PIN code in the Last Position
1389             // This effectively adds padding zeros to LSB positions when the broadcast code
1390             // is shorter than 16 octets
1391             System.arraycopy(
1392                     actualPIN, 0, res,
1393                     (BassConstants.PIN_CODE_CMD_LEN - actualPIN.length), actualPIN.length);
1394             log("SET_BCAST_PIN in Bytes");
1395             BassUtils.printByteArray(res);
1396         }
1397         return res;
1398     }
1399 
isItRightTimeToUpdateBroadcastPin(byte sourceId)1400     private boolean isItRightTimeToUpdateBroadcastPin(byte sourceId) {
1401         Collection<BluetoothLeBroadcastReceiveState> recvStates =
1402                 mBluetoothLeBroadcastReceiveStates.values();
1403         Iterator<BluetoothLeBroadcastReceiveState> iterator = recvStates.iterator();
1404         boolean retval = false;
1405         if (mForceSB) {
1406             log("force SB is set");
1407             return true;
1408         }
1409         while (iterator.hasNext()) {
1410             BluetoothLeBroadcastReceiveState state = iterator.next();
1411             if (state == null) {
1412                 log("Source state is null");
1413                 continue;
1414             }
1415             if (sourceId == state.getSourceId() && state.getBigEncryptionState()
1416                     == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED) {
1417                 retval = true;
1418                 break;
1419             }
1420         }
1421         log("IsItRightTimeToUpdateBroadcastPIN returning:" + retval);
1422         return retval;
1423     }
1424 
1425     @VisibleForTesting
1426     class Connected extends State {
1427         @Override
enter()1428         public void enter() {
1429             log("Enter Connected(" + mDevice + "): "
1430                     + messageWhatToString(getCurrentMessage().what));
1431             removeDeferredMessages(CONNECT);
1432             if (mLastConnectionState == BluetoothProfile.STATE_CONNECTED) {
1433                 log("CONNECTED->CONNECTED: Ignore");
1434                 // Broadcast for testing purpose only
1435                 if (Utils.isInstrumentationTestMode()) {
1436                     Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST");
1437                     mService.sendBroadcast(intent, BLUETOOTH_CONNECT,
1438                             Utils.getTempAllowlistBroadcastOptions());
1439                 }
1440             } else {
1441                 broadcastConnectionState(mDevice, mLastConnectionState,
1442                         BluetoothProfile.STATE_CONNECTED);
1443             }
1444         }
1445 
1446         @Override
exit()1447         public void exit() {
1448             log("Exit Connected(" + mDevice + "): "
1449                     + messageWhatToString(getCurrentMessage().what));
1450             mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
1451         }
1452 
1453         @Override
processMessage(Message message)1454         public boolean processMessage(Message message) {
1455             log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what));
1456             BluetoothLeBroadcastMetadata metaData;
1457             switch (message.what) {
1458                 case CONNECT:
1459                     Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
1460                     break;
1461                 case DISCONNECT:
1462                     log("Disconnecting from " + mDevice);
1463                     mAllowReconnect = false;
1464                     if (mBluetoothGatt != null) {
1465                         mBluetoothGatt.disconnect();
1466                         mBluetoothGatt.close();
1467                         mBluetoothGatt = null;
1468                         cancelActiveSync(null);
1469                         transitionTo(mDisconnected);
1470                     } else {
1471                         log("mBluetoothGatt is null");
1472                     }
1473                     break;
1474                 case CONNECTION_STATE_CHANGED:
1475                     int state = (int) message.obj;
1476                     Log.w(TAG, "Connected:connection state changed:" + state);
1477                     if (state == BluetoothProfile.STATE_CONNECTED) {
1478                         Log.w(TAG, "device is already connected to Bass" + mDevice);
1479                     } else {
1480                         Log.w(TAG, "unexpected disconnected from " + mDevice);
1481                         cancelActiveSync(null);
1482                         transitionTo(mDisconnected);
1483                     }
1484                     break;
1485                 case READ_BASS_CHARACTERISTICS:
1486                     BluetoothGattCharacteristic characteristic =
1487                             (BluetoothGattCharacteristic) message.obj;
1488                     if (mBluetoothGatt != null) {
1489                         mBluetoothGatt.readCharacteristic(characteristic);
1490                         transitionTo(mConnectedProcessing);
1491                     } else {
1492                         Log.e(TAG, "READ_BASS_CHARACTERISTICS is ignored, Gatt handle is null");
1493                     }
1494                     break;
1495                 case START_SCAN_OFFLOAD:
1496                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1497                         mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START);
1498                         mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1499                         mPendingOperation = message.what;
1500                         transitionTo(mConnectedProcessing);
1501                     } else {
1502                         log("no Bluetooth Gatt handle, may need to fetch write");
1503                     }
1504                     break;
1505                 case STOP_SCAN_OFFLOAD:
1506                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1507                         mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP);
1508                         mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1509                         mPendingOperation = message.what;
1510                         transitionTo(mConnectedProcessing);
1511                     } else {
1512                         log("no Bluetooth Gatt handle, may need to fetch write");
1513                     }
1514                     break;
1515                 case SELECT_BCAST_SOURCE:
1516                     ScanResult scanRes = (ScanResult) message.obj;
1517                     boolean auto = ((int) message.arg1) == BassConstants.AUTO;
1518                     selectSource(scanRes, auto);
1519                     break;
1520                 case ADD_BCAST_SOURCE:
1521                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
1522                     log("Adding Broadcast source" + metaData);
1523                     byte[] addSourceInfo = convertMetadataToAddSourceByteArray(metaData);
1524                     if (addSourceInfo == null) {
1525                         Log.e(TAG, "add source: source Info is NULL");
1526                         break;
1527                     }
1528                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1529                         mBroadcastScanControlPoint.setValue(addSourceInfo);
1530                         mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1531                         mPendingOperation = message.what;
1532                         mPendingMetadata = metaData;
1533                         if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
1534                             mSetBroadcastCodePending = true;
1535                         }
1536                         transitionTo(mConnectedProcessing);
1537                         sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS);
1538                     } else {
1539                         Log.e(TAG, "ADD_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
1540                         mService.getCallbacks().notifySourceAddFailed(mDevice,
1541                                 metaData, BluetoothStatusCodes.ERROR_UNKNOWN);
1542                     }
1543                     break;
1544                 case UPDATE_BCAST_SOURCE:
1545                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
1546                     int sourceId = message.arg1;
1547                     int paSync = message.arg2;
1548                     log("Updating Broadcast source" + metaData);
1549                     byte[] updateSourceInfo = convertBroadcastMetadataToUpdateSourceByteArray(
1550                             sourceId, metaData, paSync);
1551                     if (updateSourceInfo == null) {
1552                         Log.e(TAG, "update source: source Info is NULL");
1553                         break;
1554                     }
1555                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1556                         mBroadcastScanControlPoint.setValue(updateSourceInfo);
1557                         mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1558                         mPendingOperation = message.what;
1559                         mPendingSourceId = (byte) sourceId;
1560                         if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) {
1561                             setPendingRemove(sourceId, true);
1562                         }
1563                         if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
1564                             mSetBroadcastCodePending = true;
1565                         }
1566                         mPendingMetadata = metaData;
1567                         transitionTo(mConnectedProcessing);
1568                         sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS);
1569                     } else {
1570                         Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
1571                         mService.getCallbacks().notifySourceModifyFailed(
1572                                 mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN);
1573                     }
1574                     break;
1575                 case SET_BCAST_CODE:
1576                     BluetoothLeBroadcastReceiveState recvState =
1577                             (BluetoothLeBroadcastReceiveState) message.obj;
1578                     log("SET_BCAST_CODE metaData: " + recvState);
1579                     if (!isItRightTimeToUpdateBroadcastPin((byte) recvState.getSourceId())) {
1580                         mSetBroadcastCodePending = true;
1581                         mSetBroadcastPINRcvState = recvState;
1582                         log("Ignore SET_BCAST now, but store it for later");
1583                     } else {
1584                         byte[] setBroadcastPINcmd =
1585                                 convertRecvStateToSetBroadcastCodeByteArray(recvState);
1586                         if (setBroadcastPINcmd == null) {
1587                             Log.e(TAG, "SET_BCAST_CODE: Broadcast code is NULL");
1588                             break;
1589                         }
1590                         if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1591                             mBroadcastScanControlPoint.setValue(setBroadcastPINcmd);
1592                             mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1593                             mPendingOperation = message.what;
1594                             mPendingSourceId = (byte) recvState.getSourceId();
1595                             transitionTo(mConnectedProcessing);
1596                             sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS);
1597                             mSetBroadcastCodePending = false;
1598                             mSetBroadcastPINRcvState = null;
1599                         }
1600                     }
1601                     break;
1602                 case REMOVE_BCAST_SOURCE:
1603                     byte sid = (byte) message.arg1;
1604                     log("Removing Broadcast source, sourceId: " + sid);
1605                     byte[] removeSourceInfo = new byte[2];
1606                     removeSourceInfo[0] = OPCODE_REMOVE_SOURCE;
1607                     removeSourceInfo[1] = sid;
1608                     if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) {
1609                         if (isPendingRemove((int) sid)) {
1610                             setPendingRemove((int) sid, false);
1611                         }
1612 
1613                         mBroadcastScanControlPoint.setValue(removeSourceInfo);
1614                         mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint);
1615                         mPendingOperation = message.what;
1616                         mPendingSourceId = sid;
1617                         transitionTo(mConnectedProcessing);
1618                         sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS);
1619                     } else {
1620                         Log.e(TAG, "REMOVE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal");
1621                         mService.getCallbacks().notifySourceRemoveFailed(mDevice,
1622                                 sid, BluetoothStatusCodes.ERROR_UNKNOWN);
1623                     }
1624                     break;
1625                 case PSYNC_ACTIVE_TIMEOUT:
1626                     cancelActiveSync(null);
1627                     break;
1628                 default:
1629                     log("CONNECTED: not handled message:" + message.what);
1630                     return NOT_HANDLED;
1631             }
1632             return HANDLED;
1633         }
1634     }
1635 
isSuccess(int status)1636     private boolean isSuccess(int status) {
1637         boolean ret = false;
1638         switch (status) {
1639             case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST:
1640             case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST:
1641             case BluetoothStatusCodes.REASON_REMOTE_REQUEST:
1642             case BluetoothStatusCodes.REASON_SYSTEM_POLICY:
1643                 ret = true;
1644                 break;
1645             default:
1646                 break;
1647         }
1648         return ret;
1649     }
1650 
sendPendingCallbacks(int pendingOp, int status)1651     void sendPendingCallbacks(int pendingOp, int status) {
1652         switch (pendingOp) {
1653             case START_SCAN_OFFLOAD:
1654                 if (!isSuccess(status)) {
1655                     if (!mAutoTriggered) {
1656                         cancelActiveSync(null);
1657                     } else {
1658                         mAutoTriggered = false;
1659                     }
1660                 }
1661                 break;
1662             case ADD_BCAST_SOURCE:
1663                 if (!isSuccess(status)) {
1664                     cancelActiveSync(null);
1665                     Message message = obtainMessage(STOP_SCAN_OFFLOAD);
1666                     sendMessage(message);
1667                     mService.getCallbacks().notifySourceAddFailed(mDevice,
1668                             mPendingMetadata, status);
1669                     mPendingMetadata = null;
1670                 }
1671                 break;
1672             case UPDATE_BCAST_SOURCE:
1673                 if (!mAutoTriggered) {
1674                     if (!isSuccess(status)) {
1675                         mService.getCallbacks().notifySourceModifyFailed(mDevice,
1676                                 mPendingSourceId, status);
1677                         mPendingMetadata = null;
1678                     }
1679                 } else {
1680                     mAutoTriggered = false;
1681                 }
1682                 break;
1683             case REMOVE_BCAST_SOURCE:
1684                 if (!isSuccess(status)) {
1685                     mService.getCallbacks().notifySourceRemoveFailed(mDevice,
1686                             mPendingSourceId, status);
1687                 }
1688                 break;
1689             case SET_BCAST_CODE:
1690                 log("sendPendingCallbacks: SET_BCAST_CODE");
1691                 break;
1692             default:
1693                 log("sendPendingCallbacks: unhandled case");
1694                 break;
1695         }
1696     }
1697 
1698     // public for testing, but private for non-testing
1699     @VisibleForTesting
1700     class ConnectedProcessing extends State {
1701         @Override
enter()1702         public void enter() {
1703             log("Enter ConnectedProcessing(" + mDevice + "): "
1704                     + messageWhatToString(getCurrentMessage().what));
1705 
1706             // Broadcast for testing purpose only
1707             if (Utils.isInstrumentationTestMode()) {
1708                 Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST");
1709                 mService.sendBroadcast(intent, BLUETOOTH_CONNECT,
1710                         Utils.getTempAllowlistBroadcastOptions());
1711             }
1712         }
1713         @Override
exit()1714         public void exit() {
1715             /* Pending Metadata will be used to bond with source ID in receiver state notify */
1716             if (mPendingOperation == REMOVE_BCAST_SOURCE) {
1717                     mPendingMetadata = null;
1718             }
1719 
1720             log("Exit ConnectedProcessing(" + mDevice + "): "
1721                     + messageWhatToString(getCurrentMessage().what));
1722         }
1723         @Override
processMessage(Message message)1724         public boolean processMessage(Message message) {
1725             log("ConnectedProcessing process message(" + mDevice + "): "
1726                     + messageWhatToString(message.what));
1727             switch (message.what) {
1728                 case CONNECT:
1729                     Log.w(TAG, "CONNECT request is ignored" + mDevice);
1730                     break;
1731                 case DISCONNECT:
1732                     Log.w(TAG, "DISCONNECT requested!: " + mDevice);
1733                     mAllowReconnect = false;
1734                     if (mBluetoothGatt != null) {
1735                         mBluetoothGatt.disconnect();
1736                         mBluetoothGatt.close();
1737                         mBluetoothGatt = null;
1738                         cancelActiveSync(null);
1739                         transitionTo(mDisconnected);
1740                     } else {
1741                         log("mBluetoothGatt is null");
1742                     }
1743                     break;
1744                 case READ_BASS_CHARACTERISTICS:
1745                     Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice);
1746                     deferMessage(message);
1747                     break;
1748                 case CONNECTION_STATE_CHANGED:
1749                     int state = (int) message.obj;
1750                     Log.w(TAG, "ConnectedProcessing: connection state changed:" + state);
1751                     if (state == BluetoothProfile.STATE_CONNECTED) {
1752                         Log.w(TAG, "should never happen from this state");
1753                     } else {
1754                         Log.w(TAG, "Unexpected disconnection " + mDevice);
1755                         transitionTo(mDisconnected);
1756                     }
1757                     break;
1758                 case GATT_TXN_PROCESSED:
1759                     removeMessages(GATT_TXN_TIMEOUT);
1760                     int status = (int) message.arg1;
1761                     log("GATT transaction processed for" + mDevice);
1762                     if (status == BluetoothGatt.GATT_SUCCESS) {
1763                         sendPendingCallbacks(
1764                                 mPendingOperation,
1765                                 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
1766                     } else {
1767                         sendPendingCallbacks(
1768                                 mPendingOperation,
1769                                 BluetoothStatusCodes.ERROR_UNKNOWN);
1770                     }
1771                     transitionTo(mConnected);
1772                     break;
1773                 case GATT_TXN_TIMEOUT:
1774                     log("GATT transaction timeout for" + mDevice);
1775                     sendPendingCallbacks(
1776                             mPendingOperation,
1777                             BluetoothStatusCodes.ERROR_UNKNOWN);
1778                     mPendingOperation = -1;
1779                     mPendingSourceId = -1;
1780                     transitionTo(mConnected);
1781                     break;
1782                 case START_SCAN_OFFLOAD:
1783                 case STOP_SCAN_OFFLOAD:
1784                 case SELECT_BCAST_SOURCE:
1785                 case ADD_BCAST_SOURCE:
1786                 case SET_BCAST_CODE:
1787                 case REMOVE_BCAST_SOURCE:
1788                 case PSYNC_ACTIVE_TIMEOUT:
1789                     log("defer the message:" + message.what + "so that it will be processed later");
1790                     deferMessage(message);
1791                     break;
1792                 default:
1793                     log("CONNECTEDPROCESSING: not handled message:" + message.what);
1794                     return NOT_HANDLED;
1795             }
1796             return HANDLED;
1797         }
1798     }
1799 
broadcastConnectionState(BluetoothDevice device, int fromState, int toState)1800     void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) {
1801         log("broadcastConnectionState " + device + ": " + fromState + "->" + toState);
1802         if (fromState == BluetoothProfile.STATE_CONNECTED
1803                 && toState == BluetoothProfile.STATE_CONNECTED) {
1804             log("CONNECTED->CONNECTED: Ignore");
1805             return;
1806         }
1807 
1808         Intent intent = new Intent(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
1809         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
1810         intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
1811         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
1812         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1813                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1814         mService.sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
1815     }
1816 
getConnectionState()1817     int getConnectionState() {
1818         String currentState = "Unknown";
1819         if (getCurrentState() != null) {
1820             currentState = getCurrentState().getName();
1821         }
1822         switch (currentState) {
1823             case "Disconnected":
1824                 log("Disconnected");
1825                 return BluetoothProfile.STATE_DISCONNECTED;
1826             case "Connecting":
1827                 log("Connecting");
1828                 return BluetoothProfile.STATE_CONNECTING;
1829             case "Connected":
1830             case "ConnectedProcessing":
1831                 log("connected");
1832                 return BluetoothProfile.STATE_CONNECTED;
1833             default:
1834                 Log.e(TAG, "Bad currentState: " + currentState);
1835                 return BluetoothProfile.STATE_DISCONNECTED;
1836         }
1837     }
1838 
getMaximumSourceCapacity()1839     int getMaximumSourceCapacity() {
1840         return mNumOfBroadcastReceiverStates;
1841     }
1842 
getDevice()1843     BluetoothDevice getDevice() {
1844         return mDevice;
1845     }
1846 
isConnected()1847     synchronized boolean isConnected() {
1848         return getCurrentState() == mConnected;
1849     }
1850 
messageWhatToString(int what)1851     public static String messageWhatToString(int what) {
1852         switch (what) {
1853             case CONNECT:
1854                 return "CONNECT";
1855             case DISCONNECT:
1856                 return "DISCONNECT";
1857             case CONNECTION_STATE_CHANGED:
1858                 return "CONNECTION_STATE_CHANGED";
1859             case GATT_TXN_PROCESSED:
1860                 return "GATT_TXN_PROCESSED";
1861             case READ_BASS_CHARACTERISTICS:
1862                 return "READ_BASS_CHARACTERISTICS";
1863             case START_SCAN_OFFLOAD:
1864                 return "START_SCAN_OFFLOAD";
1865             case STOP_SCAN_OFFLOAD:
1866                 return "STOP_SCAN_OFFLOAD";
1867             case ADD_BCAST_SOURCE:
1868                 return "ADD_BCAST_SOURCE";
1869             case SELECT_BCAST_SOURCE:
1870                 return "SELECT_BCAST_SOURCE";
1871             case UPDATE_BCAST_SOURCE:
1872                 return "UPDATE_BCAST_SOURCE";
1873             case SET_BCAST_CODE:
1874                 return "SET_BCAST_CODE";
1875             case REMOVE_BCAST_SOURCE:
1876                 return "REMOVE_BCAST_SOURCE";
1877             case PSYNC_ACTIVE_TIMEOUT:
1878                 return "PSYNC_ACTIVE_TIMEOUT";
1879             case CONNECT_TIMEOUT:
1880                 return "CONNECT_TIMEOUT";
1881             default:
1882                 break;
1883         }
1884         return Integer.toString(what);
1885     }
1886 
1887     /**
1888      * Dump info
1889      */
dump(StringBuilder sb)1890     public void dump(StringBuilder sb) {
1891         ProfileService.println(sb, "mDevice: " + mDevice);
1892         ProfileService.println(sb, "  StateMachine: " + this);
1893         // Dump the state machine logs
1894         StringWriter stringWriter = new StringWriter();
1895         PrintWriter printWriter = new PrintWriter(stringWriter);
1896         super.dump(new FileDescriptor(), printWriter, new String[] {});
1897         printWriter.flush();
1898         stringWriter.flush();
1899         ProfileService.println(sb, "  StateMachineLog:");
1900         Scanner scanner = new Scanner(stringWriter.toString());
1901         while (scanner.hasNextLine()) {
1902             String line = scanner.nextLine();
1903             ProfileService.println(sb, "    " + line);
1904         }
1905         scanner.close();
1906     }
1907 
1908     @Override
log(String msg)1909     protected void log(String msg) {
1910         if (BassConstants.BASS_DBG) {
1911             super.log(msg);
1912         }
1913     }
1914 
logByteArray(String prefix, byte[] value, int offset, int count)1915     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
1916         StringBuilder builder = new StringBuilder(prefix);
1917         for (int i = offset; i < count; i++) {
1918             builder.append(String.format("0x%02X", value[i]));
1919             if (i != value.length - 1) {
1920                 builder.append(", ");
1921             }
1922         }
1923         Log.d(TAG, builder.toString());
1924     }
1925 
1926     /** Mockable wrapper of {@link BluetoothGatt}. */
1927     @VisibleForTesting
1928     public static class BluetoothGattTestableWrapper {
1929         public final BluetoothGatt mWrappedBluetoothGatt;
1930 
BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt)1931         BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt) {
1932             mWrappedBluetoothGatt = bluetoothGatt;
1933         }
1934 
1935         /** See {@link BluetoothGatt#getServices()}. */
getServices()1936         public List<BluetoothGattService> getServices() {
1937             return mWrappedBluetoothGatt.getServices();
1938         }
1939 
1940         /** See {@link BluetoothGatt#getService(UUID)}. */
1941         @Nullable
getService(UUID uuid)1942         public BluetoothGattService getService(UUID uuid) {
1943             return mWrappedBluetoothGatt.getService(uuid);
1944         }
1945 
1946         /** See {@link BluetoothGatt#discoverServices()}. */
discoverServices()1947         public boolean discoverServices() {
1948             return mWrappedBluetoothGatt.discoverServices();
1949         }
1950 
1951         /**
1952          * See {@link BluetoothGatt#readCharacteristic(
1953          * BluetoothGattCharacteristic)}.
1954          */
readCharacteristic(BluetoothGattCharacteristic characteristic)1955         public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
1956             return mWrappedBluetoothGatt.readCharacteristic(characteristic);
1957         }
1958 
1959         /**
1960          * See {@link BluetoothGatt#writeCharacteristic(
1961          * BluetoothGattCharacteristic, byte[], int)} .
1962          */
writeCharacteristic(BluetoothGattCharacteristic characteristic)1963         public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
1964             return mWrappedBluetoothGatt.writeCharacteristic(characteristic);
1965         }
1966 
1967         /** See {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */
readDescriptor(BluetoothGattDescriptor descriptor)1968         public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
1969             return mWrappedBluetoothGatt.readDescriptor(descriptor);
1970         }
1971 
1972         /**
1973          * See {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor,
1974          * byte[])}.
1975          */
writeDescriptor(BluetoothGattDescriptor descriptor)1976         public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
1977             return mWrappedBluetoothGatt.writeDescriptor(descriptor);
1978         }
1979 
1980         /** See {@link BluetoothGatt#requestMtu(int)}. */
requestMtu(int mtu)1981         public boolean requestMtu(int mtu) {
1982             return mWrappedBluetoothGatt.requestMtu(mtu);
1983         }
1984 
1985         /** See {@link BluetoothGatt#setCharacteristicNotification}. */
setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)1986         public boolean setCharacteristicNotification(
1987                 BluetoothGattCharacteristic characteristic, boolean enable) {
1988             return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable);
1989         }
1990 
1991         /** See {@link BluetoothGatt#disconnect()}. */
disconnect()1992         public void disconnect() {
1993             mWrappedBluetoothGatt.disconnect();
1994         }
1995 
1996         /** See {@link BluetoothGatt#close()}. */
close()1997         public void close() {
1998             mWrappedBluetoothGatt.close();
1999         }
2000     }
2001 
2002 }
2003