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 #include <base/functional/bind.h>
19 #include <base/location.h>
20 #include <bluetooth/log.h>
21 #include <hardware/bluetooth.h>
22 #include <hardware/bt_csis.h>
23 
24 #include "bind_helpers.h"
25 #include "bta_csis_api.h"
26 #include "btif_common.h"
27 #include "btif_profile_storage.h"
28 #include "stack/include/main_thread.h"
29 
30 using base::Bind;
31 using base::Owned;
32 using base::Passed;
33 using base::Unretained;
34 using bluetooth::csis::ConnectionState;
35 using bluetooth::csis::CsisClientCallbacks;
36 using bluetooth::csis::CsisClientInterface;
37 using bluetooth::csis::CsisGroupLockStatus;
38 
39 using bluetooth::csis::CsisClient;
40 using namespace bluetooth;
41 
42 namespace {
43 std::unique_ptr<CsisClientInterface> csis_client_instance;
44 std::atomic_bool initialized = false;
45 
46 class CsipSetCoordinatorServiceInterfaceImpl : public CsisClientInterface,
47                                                public CsisClientCallbacks {
48   ~CsipSetCoordinatorServiceInterfaceImpl() override = default;
49 
Init(CsisClientCallbacks * callbacks)50   void Init(CsisClientCallbacks* callbacks) override {
51     this->callbacks_ = callbacks;
52 
53     do_in_main_thread(
54         FROM_HERE,
55         Bind(&CsisClient::Initialize, this,
56              jni_thread_wrapper(Bind(&btif_storage_load_bonded_csis_devices))));
57     /* It might be not yet initialized, but setting this flag here is safe,
58      * because other calls will check this and the native instance
59      */
60     initialized = true;
61   }
62 
Connect(const RawAddress & addr)63   void Connect(const RawAddress& addr) override {
64     if (!initialized || !CsisClient::IsCsisClientRunning()) {
65       log::verbose(
66           "call ignored, due to already started cleanup procedure or service "
67           "being not read");
68       return;
69     }
70 
71     do_in_main_thread(FROM_HERE, Bind(&CsisClient::Connect,
72                                       Unretained(CsisClient::Get()), addr));
73   }
74 
Disconnect(const RawAddress & addr)75   void Disconnect(const RawAddress& addr) override {
76     if (!initialized || !CsisClient::IsCsisClientRunning()) {
77       log::verbose(
78           "call ignored, due to already started cleanup procedure or service "
79           "being not read");
80       return;
81     }
82 
83     do_in_main_thread(FROM_HERE, Bind(&CsisClient::Disconnect,
84                                       Unretained(CsisClient::Get()), addr));
85   }
86 
RemoveDevice(const RawAddress & addr)87   void RemoveDevice(const RawAddress& addr) override {
88     if (!initialized || !CsisClient::IsCsisClientRunning()) {
89       log::verbose(
90           "call ignored, due to already started cleanup procedure or service "
91           "being not ready");
92 
93       /* Clear storage */
94       do_in_jni_thread(Bind(&btif_storage_remove_csis_device, addr));
95       return;
96     }
97 
98     do_in_main_thread(FROM_HERE, Bind(&CsisClient::RemoveDevice,
99                                       Unretained(CsisClient::Get()), addr));
100     /* Clear storage */
101     do_in_jni_thread(Bind(&btif_storage_remove_csis_device, addr));
102   }
103 
LockGroup(int group_id,bool lock)104   void LockGroup(int group_id, bool lock) override {
105     if (!initialized || !CsisClient::IsCsisClientRunning()) {
106       log::verbose(
107           "call ignored, due to already started cleanup procedure or service "
108           "being not read");
109       return;
110     }
111 
112     do_in_main_thread(
113         FROM_HERE, Bind(&CsisClient::LockGroup, Unretained(CsisClient::Get()),
114                         group_id, lock, base::DoNothing()));
115   }
116 
Cleanup(void)117   void Cleanup(void) override {
118     if (!initialized || !CsisClient::IsCsisClientRunning()) {
119       log::verbose(
120           "call ignored, due to already started cleanup procedure or service "
121           "being not read");
122       return;
123     }
124 
125     initialized = false;
126     do_in_main_thread(FROM_HERE, Bind(&CsisClient::CleanUp));
127   }
128 
OnConnectionState(const RawAddress & addr,ConnectionState state)129   void OnConnectionState(const RawAddress& addr,
130                          ConnectionState state) override {
131     do_in_jni_thread(Bind(&CsisClientCallbacks::OnConnectionState,
132                           Unretained(callbacks_), addr, state));
133   }
134 
OnDeviceAvailable(const RawAddress & addr,int group_id,int group_size,int rank,const bluetooth::Uuid & uuid)135   void OnDeviceAvailable(const RawAddress& addr, int group_id, int group_size,
136                          int rank, const bluetooth::Uuid& uuid) override {
137     do_in_jni_thread(Bind(&CsisClientCallbacks::OnDeviceAvailable,
138                           Unretained(callbacks_), addr, group_id, group_size,
139                           rank, uuid));
140   }
141 
OnSetMemberAvailable(const RawAddress & addr,int group_id)142   void OnSetMemberAvailable(const RawAddress& addr, int group_id) override {
143     do_in_jni_thread(Bind(&CsisClientCallbacks::OnSetMemberAvailable,
144                           Unretained(callbacks_), addr, group_id));
145   }
146 
147   /* Callback for lock changed in the group */
OnGroupLockChanged(int group_id,bool locked,CsisGroupLockStatus status)148   virtual void OnGroupLockChanged(int group_id, bool locked,
149                                   CsisGroupLockStatus status) override {
150     do_in_jni_thread(Bind(&CsisClientCallbacks::OnGroupLockChanged,
151                           Unretained(callbacks_), group_id, locked, status));
152   }
153 
154  private:
155   CsisClientCallbacks* callbacks_;
156 };
157 
158 } /* namespace */
159 
btif_csis_client_get_interface(void)160 CsisClientInterface* btif_csis_client_get_interface(void) {
161   if (!csis_client_instance)
162     csis_client_instance.reset(new CsipSetCoordinatorServiceInterfaceImpl());
163 
164   return csis_client_instance.get();
165 }
166