1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.leaudio;
19 
20 import android.app.Application;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothLeBroadcastMetadata;
23 
24 import androidx.annotation.NonNull;
25 import androidx.lifecycle.AndroidViewModel;
26 import androidx.lifecycle.LiveData;
27 import androidx.lifecycle.MutableLiveData;
28 
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 
33 public class BroadcastScanViewModel extends AndroidViewModel {
34     private final String TAG = "BroadcastScanViewModel";
35     boolean mIsActivityScanning = false;
36     BluetoothDevice mScanDelegatorDevice;
37 
38     // TODO: Remove these variables if they are unnecessary
39     //    // AddBroadcast context
40     //    BluetoothDevice mSetSrcTargetDevice;
41     //    List<BluetoothBroadcastAudioScanBaseConfig> mSetSrcConfigs;
42     //    boolean mSetSrcSyncPa;
43 
44     BluetoothProxy mBluetooth;
45     Application mApplication;
46     private MutableLiveData<List<BluetoothLeBroadcastMetadata>> mAllBroadcasts =
47             new MutableLiveData<>();
48     private HashMap<Integer, BluetoothLeBroadcastMetadata> mScanSessionBroadcasts = new HashMap<>();
49 
50     private final BluetoothProxy.OnBassEventListener mBassEventListener =
51             new BluetoothProxy.OnBassEventListener() {
52                 @Override
53                 public void onSourceFound(BluetoothLeBroadcastMetadata source) {
54                     mScanSessionBroadcasts.put(source.getBroadcastId(), source);
55                     refreshBroadcasts();
56                 }
57 
58                 @Override
59                 public void onScanningStateChanged(boolean isScanning) {
60                     if (!isScanning) {
61                         // Update the live broadcast list and clear scan session results
62                         List<BluetoothLeBroadcastMetadata> localSessionBroadcasts =
63                                 mBluetooth.getAllLocalBroadcasts();
64                         ArrayList<BluetoothLeBroadcastMetadata> new_arr;
65                         if (localSessionBroadcasts != null) {
66                             new_arr = new ArrayList<>(localSessionBroadcasts);
67                         } else {
68                             new_arr = new ArrayList<>();
69                         }
70                         new_arr.addAll(mScanSessionBroadcasts.values());
71                         mAllBroadcasts.postValue(new_arr);
72 
73                         // Continue as long as the main activity wants
74                         if (mIsActivityScanning) {
75                             if (mScanDelegatorDevice != null) {
76                                 mBluetooth.scanForBroadcasts(mScanDelegatorDevice, true);
77                             }
78                         }
79                     } else {
80                         // FIXME: Clear won't work - it would auto-update the mutable and clear it
81                         // as
82                         // mutable uses reference to its values
83                         mScanSessionBroadcasts = new HashMap<>();
84                     }
85                 }
86             };
87 
88     private final BluetoothProxy.OnLocalBroadcastEventListener mLocalBroadcastEventListener =
89             new BluetoothProxy.OnLocalBroadcastEventListener() {
90                 @Override
91                 public void onBroadcastStarted(int broadcastId) {
92                     // FIXME: We need a finer grain control over updating individual broadcast state
93                     //        and not just the entire list of broadcasts
94                     refreshBroadcasts();
95                 }
96 
97                 @Override
98                 public void onBroadcastStopped(int broadcastId) {
99                     refreshBroadcasts();
100                 }
101 
102                 @Override
103                 public void onBroadcastUpdated(int broadcastId) {
104                     refreshBroadcasts();
105                 }
106 
107                 @Override
108                 public void onBroadcastMetadataChanged(
109                         int broadcastId, BluetoothLeBroadcastMetadata metadata) {
110                     refreshBroadcasts();
111                 }
112             };
113 
BroadcastScanViewModel(@onNull Application application)114     public BroadcastScanViewModel(@NonNull Application application) {
115         super(application);
116         mApplication = application;
117         mBluetooth = BluetoothProxy.getBluetoothProxy(application);
118 
119         mBluetooth.setOnBassEventListener(mBassEventListener);
120         mBluetooth.setOnLocalBroadcastEventListener(mLocalBroadcastEventListener);
121     }
122 
123     @Override
onCleared()124     public void onCleared() {
125         mBluetooth.setOnBassEventListener(null);
126         mBluetooth.setOnLocalBroadcastEventListener(null);
127     }
128 
getAllBroadcasts()129     public LiveData<List<BluetoothLeBroadcastMetadata>> getAllBroadcasts() {
130         return mAllBroadcasts;
131     }
132 
scanForBroadcasts(BluetoothDevice delegatorDevice, boolean scan)133     public void scanForBroadcasts(BluetoothDevice delegatorDevice, boolean scan) {
134         mIsActivityScanning = scan;
135         mScanDelegatorDevice = delegatorDevice;
136 
137         // First update the live broadcast list
138         List<BluetoothLeBroadcastMetadata> localSessionBroadcasts =
139                 mBluetooth.getAllLocalBroadcasts();
140         ArrayList<BluetoothLeBroadcastMetadata> new_arr;
141         if (localSessionBroadcasts != null) {
142             new_arr = new ArrayList<>(localSessionBroadcasts);
143         } else {
144             new_arr = new ArrayList<>();
145         }
146         new_arr.addAll(mScanSessionBroadcasts.values());
147         mAllBroadcasts.postValue(new_arr);
148 
149         mBluetooth.scanForBroadcasts(mScanDelegatorDevice, scan);
150     }
151 
addBroadcastSource( BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata)152     public void addBroadcastSource(
153             BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata) {
154         mBluetooth.addBroadcastSource(sink, sourceMetadata);
155     }
156 
refreshBroadcasts()157     public void refreshBroadcasts() {
158         // Concatenate local broadcasts to the scanned broadcast list
159         List<BluetoothLeBroadcastMetadata> localSessionBroadcasts =
160                 mBluetooth.getAllLocalBroadcasts();
161         ArrayList<BluetoothLeBroadcastMetadata> new_arr = new ArrayList<>(localSessionBroadcasts);
162         new_arr.addAll(mScanSessionBroadcasts.values());
163         mAllBroadcasts.postValue(new_arr);
164     }
165 }
166