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