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