1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "bt_shim_l2cap"
18 
19 #include "main/shim/l2c_api.h"
20 #include "gd/l2cap/le/l2cap_le_module.h"
21 #include "gd/os/log.h"
22 #include "gd/os/queue.h"
23 #include "gd/packet/raw_builder.h"
24 #include "main/shim/btm.h"
25 #include "main/shim/entry.h"
26 #include "main/shim/helpers.h"
27 #include "main/shim/l2cap.h"
28 
29 static bluetooth::shim::legacy::L2cap shim_l2cap;
30 
31 /**
32  * Classic Service Registration APIs
33  */
L2CA_Register(uint16_t client_psm,tL2CAP_APPL_INFO * callbacks,bool enable_snoop,tL2CAP_ERTM_INFO * p_ertm_info,uint16_t required_mtu)34 uint16_t bluetooth::shim::L2CA_Register(uint16_t client_psm,
35                                         tL2CAP_APPL_INFO* callbacks,
36                                         bool enable_snoop,
37                                         tL2CAP_ERTM_INFO* p_ertm_info,
38                                         uint16_t required_mtu) {
39   CHECK(callbacks != nullptr);
40 
41   if (L2C_INVALID_PSM(client_psm)) {
42     LOG_ERROR("%s Invalid classic psm:%hd", __func__, client_psm);
43     return 0;
44   }
45 
46   if ((callbacks->pL2CA_ConfigCfm_Cb == nullptr) ||
47       (callbacks->pL2CA_ConfigInd_Cb == nullptr) ||
48       (callbacks->pL2CA_DataInd_Cb == nullptr) ||
49       (callbacks->pL2CA_DisconnectInd_Cb == nullptr)) {
50     LOG_ERROR("%s Invalid classic callbacks psm:%hd", __func__, client_psm);
51     return 0;
52   }
53 
54   /**
55    * Check if this is a registration for an outgoing-only connection.
56    */
57   bool is_outgoing_connection_only = callbacks->pL2CA_ConnectInd_Cb == nullptr;
58   uint16_t psm = shim_l2cap.ConvertClientToRealPsm(client_psm,
59                                                    is_outgoing_connection_only);
60 
61   if (shim_l2cap.Classic().IsPsmRegistered(psm)) {
62     LOG_ERROR("%s Already registered classic client_psm:%hd psm:%hd", __func__,
63               client_psm, psm);
64     return 0;
65   }
66   LOG_INFO("%s classic client_psm:%hd psm:%hd", __func__, client_psm, psm);
67 
68   return shim_l2cap.RegisterService(psm, callbacks, enable_snoop, p_ertm_info,
69                                     required_mtu);
70 }
71 
L2CA_Deregister(uint16_t client_psm)72 void bluetooth::shim::L2CA_Deregister(uint16_t client_psm) {
73   if (L2C_INVALID_PSM(client_psm)) {
74     LOG_ERROR("%s Invalid classic client_psm:%hd", __func__, client_psm);
75     return;
76   }
77   uint16_t psm = shim_l2cap.ConvertClientToRealPsm(client_psm);
78 
79   shim_l2cap.UnregisterService(psm);
80   shim_l2cap.RemoveClientPsm(psm);
81 }
82 
L2CA_AllocatePSM(void)83 uint16_t bluetooth::shim::L2CA_AllocatePSM(void) {
84   return shim_l2cap.GetNextDynamicClassicPsm();
85 }
86 
L2CA_AllocateLePSM(void)87 uint16_t bluetooth::shim::L2CA_AllocateLePSM(void) {
88   return shim_l2cap.GetNextDynamicLePsm();
89 }
90 
L2CA_FreeLePSM(uint16_t psm)91 void bluetooth::shim::L2CA_FreeLePSM(uint16_t psm) {
92   if (!shim_l2cap.Le().IsPsmRegistered(psm)) {
93     LOG_ERROR("%s Not previously registered le psm:%hd", __func__, psm);
94     return;
95   }
96   shim_l2cap.Le().UnregisterPsm(psm);
97 }
98 
99 /**
100  * Classic Connection Oriented Channel APIS
101  */
L2CA_ErtmConnectReq(uint16_t psm,const RawAddress & raw_address,tL2CAP_ERTM_INFO * p_ertm_info)102 uint16_t bluetooth::shim::L2CA_ErtmConnectReq(uint16_t psm,
103                                               const RawAddress& raw_address,
104                                               tL2CAP_ERTM_INFO* p_ertm_info) {
105   return shim_l2cap.CreateConnection(psm, raw_address);
106 }
107 
L2CA_ConnectReq(uint16_t psm,const RawAddress & raw_address)108 uint16_t bluetooth::shim::L2CA_ConnectReq(uint16_t psm,
109                                           const RawAddress& raw_address) {
110   return shim_l2cap.CreateConnection(psm, raw_address);
111 }
112 
L2CA_ErtmConnectRsp(const RawAddress & p_bd_addr,uint8_t id,uint16_t lcid,uint16_t result,uint16_t status,tL2CAP_ERTM_INFO * p_ertm_info)113 bool bluetooth::shim::L2CA_ErtmConnectRsp(const RawAddress& p_bd_addr,
114                                           uint8_t id, uint16_t lcid,
115                                           uint16_t result, uint16_t status,
116                                           tL2CAP_ERTM_INFO* p_ertm_info) {
117   return shim_l2cap.ConnectResponse(p_bd_addr, id, lcid, result, status,
118                                     p_ertm_info);
119 }
120 
L2CA_ConnectRsp(const RawAddress & p_bd_addr,uint8_t id,uint16_t lcid,uint16_t result,uint16_t status)121 bool bluetooth::shim::L2CA_ConnectRsp(const RawAddress& p_bd_addr, uint8_t id,
122                                       uint16_t lcid, uint16_t result,
123                                       uint16_t status) {
124   return bluetooth::shim::L2CA_ErtmConnectRsp(p_bd_addr, id, lcid, result,
125                                               status, nullptr);
126 }
127 
L2CA_ConfigReq(uint16_t cid,tL2CAP_CFG_INFO * cfg_info)128 bool bluetooth::shim::L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* cfg_info) {
129   return shim_l2cap.ConfigRequest(cid, cfg_info);
130 }
131 
L2CA_ConfigRsp(uint16_t cid,tL2CAP_CFG_INFO * cfg_info)132 bool bluetooth::shim::L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* cfg_info) {
133   return shim_l2cap.ConfigResponse(cid, cfg_info);
134 }
135 
L2CA_DisconnectReq(uint16_t cid)136 bool bluetooth::shim::L2CA_DisconnectReq(uint16_t cid) {
137   return shim_l2cap.DisconnectRequest(cid);
138 }
139 
L2CA_DisconnectRsp(uint16_t cid)140 bool bluetooth::shim::L2CA_DisconnectRsp(uint16_t cid) {
141   return shim_l2cap.DisconnectResponse(cid);
142 }
143 
144 /**
145  * Le Connection Oriented Channel APIs
146  */
L2CA_RegisterLECoc(uint16_t psm,tL2CAP_APPL_INFO * callbacks)147 uint16_t bluetooth::shim::L2CA_RegisterLECoc(uint16_t psm,
148                                              tL2CAP_APPL_INFO* callbacks) {
149   LOG_INFO("UNIMPLEMENTED %s psm:%hd callbacks:%p", __func__, psm, callbacks);
150   return 0;
151 }
152 
L2CA_DeregisterLECoc(uint16_t psm)153 void bluetooth::shim::L2CA_DeregisterLECoc(uint16_t psm) {
154   LOG_INFO("UNIMPLEMENTED %s psm:%hd", __func__, psm);
155 }
156 
L2CA_ConnectLECocReq(uint16_t psm,const RawAddress & p_bd_addr,tL2CAP_LE_CFG_INFO * p_cfg)157 uint16_t bluetooth::shim::L2CA_ConnectLECocReq(uint16_t psm,
158                                                const RawAddress& p_bd_addr,
159                                                tL2CAP_LE_CFG_INFO* p_cfg) {
160   LOG_INFO("UNIMPLEMENTED %s psm:%hd addr:%s p_cfg:%p", __func__, psm,
161            p_bd_addr.ToString().c_str(), p_cfg);
162   return 0;
163 }
164 
L2CA_ConnectLECocRsp(const RawAddress & p_bd_addr,uint8_t id,uint16_t lcid,uint16_t result,uint16_t status,tL2CAP_LE_CFG_INFO * p_cfg)165 bool bluetooth::shim::L2CA_ConnectLECocRsp(const RawAddress& p_bd_addr,
166                                            uint8_t id, uint16_t lcid,
167                                            uint16_t result, uint16_t status,
168                                            tL2CAP_LE_CFG_INFO* p_cfg) {
169   LOG_INFO(
170       "UNIMPLEMENTED %s addr:%s id:%hhd lcid:%hd result:%hd status:%hd "
171       "p_cfg:%p",
172       __func__, p_bd_addr.ToString().c_str(), id, lcid, result, status, p_cfg);
173   return false;
174 }
175 
L2CA_GetPeerLECocConfig(uint16_t lcid,tL2CAP_LE_CFG_INFO * peer_cfg)176 bool bluetooth::shim::L2CA_GetPeerLECocConfig(uint16_t lcid,
177                                               tL2CAP_LE_CFG_INFO* peer_cfg) {
178   LOG_INFO("UNIMPLEMENTED %s lcid:%hd peer_cfg:%p", __func__, lcid, peer_cfg);
179   return false;
180 }
181 
182 /**
183  * Channel Data Writes
184  */
L2CA_SetConnectionCallbacks(uint16_t cid,const tL2CAP_APPL_INFO * callbacks)185 bool bluetooth::shim::L2CA_SetConnectionCallbacks(
186     uint16_t cid, const tL2CAP_APPL_INFO* callbacks) {
187   LOG_INFO("Unsupported API %s", __func__);
188   return false;
189 }
190 
L2CA_DataWrite(uint16_t cid,BT_HDR * p_data)191 uint8_t bluetooth::shim::L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) {
192   bool write_success = shim_l2cap.Write(cid, p_data);
193   return write_success ? L2CAP_DW_SUCCESS : L2CAP_DW_FAILED;
194 }
195 
196 /**
197  * L2cap Layer APIs
198  */
L2CA_SetDesireRole(uint8_t new_role)199 uint8_t bluetooth::shim::L2CA_SetDesireRole(uint8_t new_role) {
200   LOG_INFO("UNIMPLEMENTED %s", __func__);
201   return 0;
202 }
203 
204 /**
205  * Link APIs
206  */
L2CA_SetIdleTimeoutByBdAddr(const RawAddress & bd_addr,uint16_t timeout,tBT_TRANSPORT transport)207 bool bluetooth::shim::L2CA_SetIdleTimeoutByBdAddr(const RawAddress& bd_addr,
208                                                   uint16_t timeout,
209                                                   tBT_TRANSPORT transport) {
210   LOG_INFO("UNIMPLEMENTED %s", __func__);
211   return false;
212 }
213 
L2CA_SetAclPriority(const RawAddress & bd_addr,uint8_t priority)214 bool bluetooth::shim::L2CA_SetAclPriority(const RawAddress& bd_addr,
215                                           uint8_t priority) {
216   LOG_INFO("UNIMPLEMENTED %s", __func__);
217   return false;
218 }
219 
L2CA_SetFlushTimeout(const RawAddress & bd_addr,uint16_t flush_tout)220 bool bluetooth::shim::L2CA_SetFlushTimeout(const RawAddress& bd_addr,
221                                            uint16_t flush_tout) {
222   LOG_INFO("UNIMPLEMENTED %s", __func__);
223   return false;
224 }
225 
L2CA_GetPeerFeatures(const RawAddress & bd_addr,uint32_t * p_ext_feat,uint8_t * p_chnl_mask)226 bool bluetooth::shim::L2CA_GetPeerFeatures(const RawAddress& bd_addr,
227                                            uint32_t* p_ext_feat,
228                                            uint8_t* p_chnl_mask) {
229   LOG_INFO("UNIMPLEMENTED %s", __func__);
230   return false;
231 }
232 
233 using bluetooth::hci::AddressWithType;
234 using bluetooth::l2cap::le::FixedChannel;
235 using bluetooth::l2cap::le::FixedChannelManager;
236 using bluetooth::l2cap::le::FixedChannelService;
237 
238 static constexpr uint16_t kAttCid = 4;
239 
240 struct LeFixedChannelHelper {
LeFixedChannelHelperLeFixedChannelHelper241   LeFixedChannelHelper(uint16_t cid) : cid_(cid) {}
242 
243   uint16_t cid_;
244 
on_registration_completeLeFixedChannelHelper245   void on_registration_complete(FixedChannelManager::RegistrationResult result,
246                                 std::unique_ptr<FixedChannelService> service) {
247     if (result != FixedChannelManager::RegistrationResult::SUCCESS) {
248       LOG(ERROR) << "Channel is not registered. cid=" << +cid_;
249       return;
250     }
251     channel_service_ = std::move(service);
252   }
253 
254   std::unique_ptr<FixedChannelService> channel_service_ = nullptr;
255 
on_channel_closeLeFixedChannelHelper256   void on_channel_close(bluetooth::hci::AddressWithType device,
257                         bluetooth::hci::ErrorCode error_code) {
258     auto address = bluetooth::ToRawAddress(device.GetAddress());
259     channel_enqueue_buffer_[device] = nullptr;
260     channels_[device]->GetQueueUpEnd()->UnregisterDequeue();
261     channels_[device] = nullptr;
262     (freg_.pL2CA_FixedConn_Cb)(cid_, address, true, 0, 2);
263   }
264 
on_channel_openLeFixedChannelHelper265   void on_channel_open(std::unique_ptr<FixedChannel> channel) {
266     auto device = channel->GetDevice();
267     channel->RegisterOnCloseCallback(
268         bluetooth::shim::GetGdShimHandler(),
269         bluetooth::common::BindOnce(&LeFixedChannelHelper::on_channel_close,
270                                     bluetooth::common::Unretained(this),
271                                     device));
272     channel->Acquire();
273     channel_enqueue_buffer_[device] = std::make_unique<
274         bluetooth::os::EnqueueBuffer<bluetooth::packet::BasePacketBuilder>>(
275         channel->GetQueueUpEnd());
276     channel->GetQueueUpEnd()->RegisterDequeue(
277         bluetooth::shim::GetGdShimHandler(),
278         bluetooth::common::Bind(&LeFixedChannelHelper::on_incoming_data,
279                                 bluetooth::common::Unretained(this), device));
280     channels_[device] = std::move(channel);
281 
282     auto address = bluetooth::ToRawAddress(device.GetAddress());
283 
284     (freg_.pL2CA_FixedConn_Cb)(cid_, address, true, 0, BT_TRANSPORT_LE);
285     bluetooth::shim::Btm::StoreAddressType(
286         address, static_cast<uint8_t>(device.GetAddressType()));
287   }
288 
on_incoming_dataLeFixedChannelHelper289   void on_incoming_data(bluetooth::hci::AddressWithType remote) {
290     auto channel = channels_.find(remote);
291     if (channel == channels_.end()) {
292       LOG_ERROR("Channel is not open");
293       return;
294     }
295     auto packet = channel->second->GetQueueUpEnd()->TryDequeue();
296     std::vector<uint8_t> packet_vector(packet->begin(), packet->end());
297     BT_HDR* buffer =
298         static_cast<BT_HDR*>(osi_calloc(packet_vector.size() + sizeof(BT_HDR)));
299     std::copy(packet_vector.begin(), packet_vector.end(), buffer->data);
300     buffer->len = packet_vector.size();
301     auto address = bluetooth::ToRawAddress(remote.GetAddress());
302     freg_.pL2CA_FixedData_Cb(cid_, address, buffer);
303   }
304 
on_outgoing_connection_failLeFixedChannelHelper305   void on_outgoing_connection_fail(
306       RawAddress remote, FixedChannelManager::ConnectionResult result) {
307     LOG(ERROR) << "Outgoing connection failed";
308     freg_.pL2CA_FixedConn_Cb(cid_, remote, true, 0, BT_TRANSPORT_LE);
309   }
310 
sendLeFixedChannelHelper311   bool send(AddressWithType remote,
312             std::unique_ptr<bluetooth::packet::BasePacketBuilder> packet) {
313     auto buffer = channel_enqueue_buffer_.find(remote);
314     if (buffer == channel_enqueue_buffer_.end() || buffer->second == nullptr) {
315       LOG(ERROR) << "Channel is not open";
316       return false;
317     }
318     buffer->second->Enqueue(std::move(packet),
319                             bluetooth::shim::GetGdShimHandler());
320     return true;
321   }
322 
323   std::unordered_map<AddressWithType, std::unique_ptr<FixedChannel>> channels_;
324   std::unordered_map<AddressWithType,
325                      std::unique_ptr<bluetooth::os::EnqueueBuffer<
326                          bluetooth::packet::BasePacketBuilder>>>
327       channel_enqueue_buffer_;
328   tL2CAP_FIXED_CHNL_REG freg_;
329 };
330 
331 static LeFixedChannelHelper att_helper{4};
332 static std::unordered_map<uint16_t, LeFixedChannelHelper&>
333     le_fixed_channel_helper_{
334         {4, att_helper},
335     };
336 
337 /**
338  * Fixed Channel APIs. Note: Classic fixed channel (connectionless and BR SMP)
339  * is not supported
340  */
L2CA_RegisterFixedChannel(uint16_t cid,tL2CAP_FIXED_CHNL_REG * p_freg)341 bool bluetooth::shim::L2CA_RegisterFixedChannel(uint16_t cid,
342                                                 tL2CAP_FIXED_CHNL_REG* p_freg) {
343   if (cid != kAttCid) {
344     LOG(ERROR) << "Invalid cid: " << cid;
345     return false;
346   }
347   auto* helper = &le_fixed_channel_helper_.find(cid)->second;
348   if (helper == nullptr) {
349     LOG(ERROR) << "Can't register cid " << cid;
350     return false;
351   }
352   bluetooth::shim::GetL2capLeModule()
353       ->GetFixedChannelManager()
354       ->RegisterService(
355           cid,
356           common::BindOnce(&LeFixedChannelHelper::on_registration_complete,
357                            common::Unretained(helper)),
358           common::Bind(&LeFixedChannelHelper::on_channel_open,
359                        common::Unretained(helper)),
360           GetGdShimHandler());
361   helper->freg_ = *p_freg;
362   return true;
363 }
364 
L2CA_ConnectFixedChnl(uint16_t cid,const RawAddress & rem_bda)365 bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t cid,
366                                             const RawAddress& rem_bda) {
367   if (cid != kAttCid) {
368     LOG(ERROR) << "Invalid cid " << cid;
369     return false;
370   }
371 
372   auto* helper = &le_fixed_channel_helper_.find(cid)->second;
373   auto remote = ToAddressWithType(rem_bda, Btm::GetAddressType(rem_bda));
374   auto manager = bluetooth::shim::GetL2capLeModule()->GetFixedChannelManager();
375   manager->ConnectServices(
376       remote,
377       common::BindOnce(&LeFixedChannelHelper::on_outgoing_connection_fail,
378                        common::Unretained(helper), rem_bda),
379       GetGdShimHandler());
380   return true;
381 }
382 
L2CA_ConnectFixedChnl(uint16_t cid,const RawAddress & rem_bda,uint8_t initiating_phys)383 bool bluetooth::shim::L2CA_ConnectFixedChnl(uint16_t cid,
384                                             const RawAddress& rem_bda,
385                                             uint8_t initiating_phys) {
386   return bluetooth::shim::L2CA_ConnectFixedChnl(cid, rem_bda);
387 }
388 
MakeUniquePacket(const uint8_t * data,size_t len)389 static std::unique_ptr<bluetooth::packet::RawBuilder> MakeUniquePacket(
390     const uint8_t* data, size_t len) {
391   bluetooth::packet::RawBuilder builder;
392   std::vector<uint8_t> bytes(data, data + len);
393   auto payload = std::make_unique<bluetooth::packet::RawBuilder>();
394   payload->AddOctets(bytes);
395   return payload;
396 }
397 
L2CA_SendFixedChnlData(uint16_t cid,const RawAddress & rem_bda,BT_HDR * p_buf)398 uint16_t bluetooth::shim::L2CA_SendFixedChnlData(uint16_t cid,
399                                                  const RawAddress& rem_bda,
400                                                  BT_HDR* p_buf) {
401   if (cid != kAttCid) {
402     LOG(ERROR) << "Invalid cid " << cid;
403     return false;
404   }
405   auto* helper = &le_fixed_channel_helper_.find(cid)->second;
406   auto remote = ToAddressWithType(rem_bda, Btm::GetAddressType(rem_bda));
407   auto len = p_buf->len;
408   auto* data = p_buf->data + p_buf->offset;
409   bool sent = helper->send(remote, MakeUniquePacket(data, len));
410   return sent ? len : 0;
411 }
412 
L2CA_RemoveFixedChnl(uint16_t cid,const RawAddress & rem_bda)413 bool bluetooth::shim::L2CA_RemoveFixedChnl(uint16_t cid,
414                                            const RawAddress& rem_bda) {
415   if (cid != kAttCid) {
416     LOG(ERROR) << "Invalid cid " << cid;
417     return false;
418   }
419   auto* helper = &le_fixed_channel_helper_.find(cid)->second;
420   auto remote = ToAddressWithType(rem_bda, Btm::GetAddressType(rem_bda));
421   auto channel = helper->channels_.find(remote);
422   if (channel == helper->channels_.end() || channel->second == nullptr) {
423     LOG(ERROR) << "Channel is not open";
424     return false;
425   }
426   channel->second->Release();
427   return true;
428 }
429 
430 /**
431  * Channel hygiene APIs
432  */
L2CA_GetRemoteCid(uint16_t lcid,uint16_t * rcid)433 bool bluetooth::shim::L2CA_GetRemoteCid(uint16_t lcid, uint16_t* rcid) {
434   return shim_l2cap.GetRemoteCid(lcid, rcid);
435 }
436 
L2CA_SetIdleTimeout(uint16_t cid,uint16_t timeout,bool is_global)437 bool bluetooth::shim::L2CA_SetIdleTimeout(uint16_t cid, uint16_t timeout,
438                                           bool is_global) {
439   LOG_INFO("UNIMPLEMENTED %s", __func__);
440   return false;
441 }
442 
L2CA_SetTxPriority(uint16_t cid,tL2CAP_CHNL_PRIORITY priority)443 bool bluetooth::shim::L2CA_SetTxPriority(uint16_t cid,
444                                          tL2CAP_CHNL_PRIORITY priority) {
445   LOG_INFO("UNIMPLEMENTED %s", __func__);
446   return false;
447 }
448 
L2CA_SetFixedChannelTout(const RawAddress & rem_bda,uint16_t fixed_cid,uint16_t idle_tout)449 bool bluetooth::shim::L2CA_SetFixedChannelTout(const RawAddress& rem_bda,
450                                                uint16_t fixed_cid,
451                                                uint16_t idle_tout) {
452   LOG_INFO("UNIMPLEMENTED %s", __func__);
453   return false;
454 }
455 
L2CA_SetChnlFlushability(uint16_t cid,bool is_flushable)456 bool bluetooth::shim::L2CA_SetChnlFlushability(uint16_t cid,
457                                                bool is_flushable) {
458   LOG_INFO("UNIMPLEMENTED %s", __func__);
459   return false;
460 }
461 
L2CA_FlushChannel(uint16_t lcid,uint16_t num_to_flush)462 uint16_t bluetooth::shim::L2CA_FlushChannel(uint16_t lcid,
463                                             uint16_t num_to_flush) {
464   LOG_INFO("UNIMPLEMENTED %s", __func__);
465   return 0;
466 }
467 
L2CA_IsLinkEstablished(const RawAddress & bd_addr,tBT_TRANSPORT transport)468 bool bluetooth::shim::L2CA_IsLinkEstablished(const RawAddress& bd_addr,
469                                              tBT_TRANSPORT transport) {
470   LOG_INFO("UNIMPLEMENTED %s", __func__);
471   return true;
472 }
473