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