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