xref: /aosp_15_r20/external/uwb/src/rust/uwb_core/src/uci/response.rs (revision e0df40009cb5d71e642272d38ba1bb7ffccfce41)
1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::convert::{TryFrom, TryInto};
16 
17 use log::error;
18 
19 use crate::error::{Error, Result};
20 use crate::params::uci_packets::{
21     AndroidRadarConfigResponse, AppConfigTlv, CapTlv, CoreSetConfigResponse, DeviceConfigTlv,
22     GetDeviceInfoResponse, PowerStats, RadarConfigTlv, RawUciMessage, RfTestConfigResponse,
23     SessionHandle, SessionState, SessionUpdateControllerMulticastListRspV1Payload,
24     SessionUpdateControllerMulticastListRspV2Payload, SessionUpdateControllerMulticastResponse,
25     SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, StatusCode, UCIMajorVersion,
26     UciControlPacket,
27 };
28 use crate::uci::error::status_code_to_result;
29 
30 #[derive(Debug, Clone, PartialEq)]
31 pub(super) enum UciResponse {
32     SetLoggerMode,
33     SetNotification,
34     OpenHal,
35     CloseHal,
36     DeviceReset(Result<()>),
37     CoreGetDeviceInfo(Result<GetDeviceInfoResponse>),
38     CoreGetCapsInfo(Result<Vec<CapTlv>>),
39     CoreSetConfig(CoreSetConfigResponse),
40     CoreGetConfig(Result<Vec<DeviceConfigTlv>>),
41     CoreQueryTimeStamp(Result<u64>),
42     SessionInit(Result<Option<SessionHandle>>),
43     SessionDeinit(Result<()>),
44     SessionSetAppConfig(SetAppConfigResponse),
45     SessionGetAppConfig(Result<Vec<AppConfigTlv>>),
46     SessionGetCount(Result<u8>),
47     SessionGetState(Result<SessionState>),
48     SessionUpdateControllerMulticastList(Result<SessionUpdateControllerMulticastResponse>),
49     SessionUpdateDtTagRangingRounds(Result<SessionUpdateDtTagRangingRoundsResponse>),
50     SessionQueryMaxDataSize(Result<u16>),
51     SessionStart(Result<()>),
52     SessionStop(Result<()>),
53     SessionGetRangingCount(Result<usize>),
54     AndroidSetCountryCode(Result<()>),
55     AndroidGetPowerStats(Result<PowerStats>),
56     AndroidSetRadarConfig(AndroidRadarConfigResponse),
57     AndroidGetRadarConfig(Result<Vec<RadarConfigTlv>>),
58     RawUciCmd(Result<RawUciMessage>),
59     SendUciData(Result<()>),
60     SessionSetHybridControllerConfig(Result<()>),
61     SessionSetHybridControleeConfig(Result<()>),
62     SessionDataTransferPhaseConfig(Result<()>),
63     SessionSetRfTestConfig(RfTestConfigResponse),
64     RfTest(Result<()>),
65 }
66 
67 impl UciResponse {
need_retry(&self) -> bool68     pub fn need_retry(&self) -> bool {
69         match self {
70             Self::SetNotification | Self::OpenHal | Self::CloseHal | Self::SetLoggerMode => false,
71             Self::DeviceReset(result) => Self::matches_result_retry(result),
72             Self::CoreGetDeviceInfo(result) => Self::matches_result_retry(result),
73             Self::CoreGetCapsInfo(result) => Self::matches_result_retry(result),
74             Self::CoreGetConfig(result) => Self::matches_result_retry(result),
75             Self::CoreQueryTimeStamp(result) => Self::matches_result_retry(result),
76             Self::SessionInit(result) => Self::matches_result_retry(result),
77             Self::SessionDeinit(result) => Self::matches_result_retry(result),
78             Self::SessionGetAppConfig(result) => Self::matches_result_retry(result),
79             Self::SessionGetCount(result) => Self::matches_result_retry(result),
80             Self::SessionGetState(result) => Self::matches_result_retry(result),
81             Self::SessionUpdateControllerMulticastList(result) => {
82                 Self::matches_result_retry(result)
83             }
84             Self::SessionUpdateDtTagRangingRounds(result) => Self::matches_result_retry(result),
85             Self::SessionStart(result) => Self::matches_result_retry(result),
86             Self::SessionStop(result) => Self::matches_result_retry(result),
87             Self::SessionGetRangingCount(result) => Self::matches_result_retry(result),
88             Self::AndroidSetCountryCode(result) => Self::matches_result_retry(result),
89             Self::AndroidGetPowerStats(result) => Self::matches_result_retry(result),
90             Self::AndroidGetRadarConfig(result) => Self::matches_result_retry(result),
91             Self::AndroidSetRadarConfig(resp) => Self::matches_status_retry(&resp.status),
92             Self::RawUciCmd(result) => Self::matches_result_retry(result),
93             Self::SessionSetHybridControllerConfig(result) => Self::matches_result_retry(result),
94             Self::SessionSetHybridControleeConfig(result) => Self::matches_result_retry(result),
95             Self::SessionDataTransferPhaseConfig(result) => Self::matches_result_retry(result),
96             Self::CoreSetConfig(resp) => Self::matches_status_retry(&resp.status),
97             Self::SessionSetAppConfig(resp) => Self::matches_status_retry(&resp.status),
98 
99             Self::SessionQueryMaxDataSize(result) => Self::matches_result_retry(result),
100             Self::SessionSetRfTestConfig(resp) => Self::matches_status_retry(&resp.status),
101             Self::RfTest(result) => Self::matches_result_retry(result),
102             // TODO(b/273376343): Implement retry logic for Data packet send.
103             Self::SendUciData(_result) => false,
104         }
105     }
106 
matches_result_retry<T>(result: &Result<T>) -> bool107     fn matches_result_retry<T>(result: &Result<T>) -> bool {
108         matches!(result, Err(Error::CommandRetry))
109     }
matches_status_retry(status: &StatusCode) -> bool110     fn matches_status_retry(status: &StatusCode) -> bool {
111         matches!(status, StatusCode::UciStatusCommandRetry)
112     }
113 }
114 
115 impl TryFrom<(uwb_uci_packets::UciResponse, UCIMajorVersion, bool)> for UciResponse {
116     type Error = Error;
try_from( pair: (uwb_uci_packets::UciResponse, UCIMajorVersion, bool), ) -> std::result::Result<Self, Self::Error>117     fn try_from(
118         pair: (uwb_uci_packets::UciResponse, UCIMajorVersion, bool),
119     ) -> std::result::Result<Self, Self::Error> {
120         let evt = pair.0;
121         let uci_fira_major_ver = pair.1;
122         let is_multicast_list_rsp_v2_supported = pair.2;
123         use uwb_uci_packets::UciResponseChild;
124         match evt.specialize() {
125             UciResponseChild::CoreResponse(evt) => evt.try_into(),
126             UciResponseChild::SessionConfigResponse(evt) => {
127                 (evt, uci_fira_major_ver, is_multicast_list_rsp_v2_supported).try_into()
128             }
129             UciResponseChild::SessionControlResponse(evt) => evt.try_into(),
130             UciResponseChild::AndroidResponse(evt) => evt.try_into(),
131             UciResponseChild::TestResponse(evt) => evt.try_into(),
132             UciResponseChild::UciVendor_9_Response(evt) => raw_response(evt.into()),
133             UciResponseChild::UciVendor_A_Response(evt) => raw_response(evt.into()),
134             UciResponseChild::UciVendor_B_Response(evt) => raw_response(evt.into()),
135             UciResponseChild::UciVendor_E_Response(evt) => raw_response(evt.into()),
136             UciResponseChild::UciVendor_F_Response(evt) => raw_response(evt.into()),
137             _ => Err(Error::Unknown),
138         }
139     }
140 }
141 
142 impl TryFrom<uwb_uci_packets::CoreResponse> for UciResponse {
143     type Error = Error;
try_from(evt: uwb_uci_packets::CoreResponse) -> std::result::Result<Self, Self::Error>144     fn try_from(evt: uwb_uci_packets::CoreResponse) -> std::result::Result<Self, Self::Error> {
145         use uwb_uci_packets::CoreResponseChild;
146         match evt.specialize() {
147             CoreResponseChild::GetDeviceInfoRsp(evt) => Ok(UciResponse::CoreGetDeviceInfo(
148                 status_code_to_result(evt.get_status()).map(|_| GetDeviceInfoResponse {
149                     status: evt.get_status(),
150                     uci_version: evt.get_uci_version(),
151                     mac_version: evt.get_mac_version(),
152                     phy_version: evt.get_phy_version(),
153                     uci_test_version: evt.get_uci_test_version(),
154                     vendor_spec_info: evt.get_vendor_spec_info().clone(),
155                 }),
156             )),
157             CoreResponseChild::GetCapsInfoRsp(evt) => Ok(UciResponse::CoreGetCapsInfo(
158                 status_code_to_result(evt.get_status()).map(|_| evt.get_tlvs().clone()),
159             )),
160             CoreResponseChild::DeviceResetRsp(evt) => {
161                 Ok(UciResponse::DeviceReset(status_code_to_result(evt.get_status())))
162             }
163             CoreResponseChild::SetConfigRsp(evt) => {
164                 Ok(UciResponse::CoreSetConfig(CoreSetConfigResponse {
165                     status: evt.get_status(),
166                     config_status: evt.get_cfg_status().clone(),
167                 }))
168             }
169 
170             CoreResponseChild::GetConfigRsp(evt) => Ok(UciResponse::CoreGetConfig(
171                 status_code_to_result(evt.get_status()).map(|_| evt.get_tlvs().clone()),
172             )),
173             CoreResponseChild::CoreQueryTimeStampRsp(evt) => Ok(UciResponse::CoreQueryTimeStamp(
174                 status_code_to_result(evt.get_status()).map(|_| evt.get_timeStamp()),
175             )),
176             _ => Err(Error::Unknown),
177         }
178     }
179 }
180 
181 impl TryFrom<(uwb_uci_packets::SessionConfigResponse, UCIMajorVersion, bool)> for UciResponse {
182     type Error = Error;
try_from( pair: (uwb_uci_packets::SessionConfigResponse, UCIMajorVersion, bool), ) -> std::result::Result<Self, Self::Error>183     fn try_from(
184         pair: (uwb_uci_packets::SessionConfigResponse, UCIMajorVersion, bool),
185     ) -> std::result::Result<Self, Self::Error> {
186         use uwb_uci_packets::SessionConfigResponseChild;
187         let evt = pair.0;
188         let uci_fira_major_ver = pair.1;
189         let is_multicast_list_rsp_v2_supported = pair.2;
190         match evt.specialize() {
191             SessionConfigResponseChild::SessionInitRsp(evt) => {
192                 Ok(UciResponse::SessionInit(status_code_to_result(evt.get_status()).map(|_| None)))
193             }
194             SessionConfigResponseChild::SessionInitRsp_V2(evt) => Ok(UciResponse::SessionInit(
195                 status_code_to_result(evt.get_status()).map(|_| Some(evt.get_session_handle())),
196             )),
197             SessionConfigResponseChild::SessionDeinitRsp(evt) => {
198                 Ok(UciResponse::SessionDeinit(status_code_to_result(evt.get_status())))
199             }
200             SessionConfigResponseChild::SessionGetCountRsp(evt) => {
201                 Ok(UciResponse::SessionGetCount(
202                     status_code_to_result(evt.get_status()).map(|_| evt.get_session_count()),
203                 ))
204             }
205             SessionConfigResponseChild::SessionGetStateRsp(evt) => {
206                 Ok(UciResponse::SessionGetState(
207                     status_code_to_result(evt.get_status()).map(|_| evt.get_session_state()),
208                 ))
209             }
210             SessionConfigResponseChild::SessionUpdateControllerMulticastListRsp(evt)
211                 if uci_fira_major_ver == UCIMajorVersion::V1
212                     || !is_multicast_list_rsp_v2_supported =>
213             {
214                 error!(
215                     "Tryfrom: SessionConfigResponse:: SessionUpdateControllerMulticastListRspV1 "
216                 );
217                 let payload = evt.get_payload();
218                 let multicast_update_list_rsp_payload_v1 =
219                     SessionUpdateControllerMulticastListRspV1Payload::parse(payload).map_err(
220                         |e| {
221                             error!(
222                                 "Failed to parse Multicast list ntf v1 {:?}, payload: {:?}",
223                                 e, &payload
224                             );
225                             Error::BadParameters
226                         },
227                     )?;
228 
229                 Ok(UciResponse::SessionUpdateControllerMulticastList(Ok(
230                     SessionUpdateControllerMulticastResponse {
231                         status: multicast_update_list_rsp_payload_v1.status,
232                         status_list: vec![],
233                     },
234                 )))
235             }
236             SessionConfigResponseChild::SessionUpdateControllerMulticastListRsp(evt)
237                 if uci_fira_major_ver == UCIMajorVersion::V2 =>
238             {
239                 error!(
240                     "Tryfrom: SessionConfigResponse:: SessionUpdateControllerMulticastListRspV2 "
241                 );
242                 let payload = evt.get_payload();
243                 let multicast_update_list_rsp_payload_v2 =
244                     SessionUpdateControllerMulticastListRspV2Payload::parse(payload).map_err(
245                         |e| {
246                             error!(
247                                 "Failed to parse Multicast list ntf v1 {:?}, payload: {:?}",
248                                 e, &payload
249                             );
250                             Error::BadParameters
251                         },
252                     )?;
253                 Ok(UciResponse::SessionUpdateControllerMulticastList(Ok(
254                     SessionUpdateControllerMulticastResponse {
255                         status: multicast_update_list_rsp_payload_v2.status,
256                         status_list: multicast_update_list_rsp_payload_v2.controlee_status,
257                     },
258                 )))
259             }
260             SessionConfigResponseChild::SessionUpdateDtTagRangingRoundsRsp(evt) => {
261                 Ok(UciResponse::SessionUpdateDtTagRangingRounds(Ok(
262                     SessionUpdateDtTagRangingRoundsResponse {
263                         status: evt.get_status(),
264                         ranging_round_indexes: evt.get_ranging_round_indexes().to_vec(),
265                     },
266                 )))
267             }
268             SessionConfigResponseChild::SessionSetAppConfigRsp(evt) => {
269                 Ok(UciResponse::SessionSetAppConfig(SetAppConfigResponse {
270                     status: evt.get_status(),
271                     config_status: evt.get_cfg_status().clone(),
272                 }))
273             }
274             SessionConfigResponseChild::SessionGetAppConfigRsp(evt) => {
275                 Ok(UciResponse::SessionGetAppConfig(
276                     status_code_to_result(evt.get_status()).map(|_| {
277                         evt.get_tlvs().clone().into_iter().map(|tlv| tlv.into()).collect()
278                     }),
279                 ))
280             }
281             SessionConfigResponseChild::SessionQueryMaxDataSizeRsp(evt) => {
282                 Ok(UciResponse::SessionQueryMaxDataSize(
283                     status_code_to_result(evt.get_status()).map(|_| evt.get_max_data_size()),
284                 ))
285             }
286             SessionConfigResponseChild::SessionSetHybridControllerConfigRsp(evt) => {
287                 Ok(UciResponse::SessionSetHybridControllerConfig(status_code_to_result(
288                     evt.get_status(),
289                 )))
290             }
291             SessionConfigResponseChild::SessionSetHybridControleeConfigRsp(evt) => {
292                 Ok(UciResponse::SessionSetHybridControleeConfig(status_code_to_result(
293                     evt.get_status(),
294                 )))
295             }
296             SessionConfigResponseChild::SessionDataTransferPhaseConfigRsp(evt) => {
297                 Ok(UciResponse::SessionDataTransferPhaseConfig(status_code_to_result(
298                     evt.get_status(),
299                 )))
300             }
301             _ => Err(Error::Unknown),
302         }
303     }
304 }
305 
306 impl TryFrom<uwb_uci_packets::SessionControlResponse> for UciResponse {
307     type Error = Error;
try_from( evt: uwb_uci_packets::SessionControlResponse, ) -> std::result::Result<Self, Self::Error>308     fn try_from(
309         evt: uwb_uci_packets::SessionControlResponse,
310     ) -> std::result::Result<Self, Self::Error> {
311         use uwb_uci_packets::SessionControlResponseChild;
312         match evt.specialize() {
313             SessionControlResponseChild::SessionStartRsp(evt) => {
314                 Ok(UciResponse::SessionStart(status_code_to_result(evt.get_status())))
315             }
316             SessionControlResponseChild::SessionStopRsp(evt) => {
317                 Ok(UciResponse::SessionStop(status_code_to_result(evt.get_status())))
318             }
319             SessionControlResponseChild::SessionGetRangingCountRsp(evt) => {
320                 Ok(UciResponse::SessionGetRangingCount(
321                     status_code_to_result(evt.get_status()).map(|_| evt.get_count() as usize),
322                 ))
323             }
324             _ => Err(Error::Unknown),
325         }
326     }
327 }
328 
329 impl TryFrom<uwb_uci_packets::AndroidResponse> for UciResponse {
330     type Error = Error;
try_from(evt: uwb_uci_packets::AndroidResponse) -> std::result::Result<Self, Self::Error>331     fn try_from(evt: uwb_uci_packets::AndroidResponse) -> std::result::Result<Self, Self::Error> {
332         use uwb_uci_packets::AndroidResponseChild;
333         match evt.specialize() {
334             AndroidResponseChild::AndroidSetCountryCodeRsp(evt) => {
335                 Ok(UciResponse::AndroidSetCountryCode(status_code_to_result(evt.get_status())))
336             }
337             AndroidResponseChild::AndroidGetPowerStatsRsp(evt) => {
338                 Ok(UciResponse::AndroidGetPowerStats(
339                     status_code_to_result(evt.get_stats().status).map(|_| evt.get_stats().clone()),
340                 ))
341             }
342             AndroidResponseChild::AndroidSetRadarConfigRsp(evt) => {
343                 Ok(UciResponse::AndroidSetRadarConfig(AndroidRadarConfigResponse {
344                     status: evt.get_status(),
345                     config_status: evt.get_cfg_status().clone(),
346                 }))
347             }
348             AndroidResponseChild::AndroidGetRadarConfigRsp(evt) => {
349                 Ok(UciResponse::AndroidGetRadarConfig(
350                     status_code_to_result(evt.get_status()).map(|_| evt.get_tlvs().clone()),
351                 ))
352             }
353             _ => Err(Error::Unknown),
354         }
355     }
356 }
357 
358 impl TryFrom<uwb_uci_packets::TestResponse> for UciResponse {
359     type Error = Error;
try_from(evt: uwb_uci_packets::TestResponse) -> std::result::Result<Self, Self::Error>360     fn try_from(evt: uwb_uci_packets::TestResponse) -> std::result::Result<Self, Self::Error> {
361         use uwb_uci_packets::TestResponseChild;
362         match evt.specialize() {
363             TestResponseChild::SessionSetRfTestConfigRsp(evt) => {
364                 Ok(UciResponse::SessionSetRfTestConfig(RfTestConfigResponse {
365                     status: evt.get_status(),
366                     config_status: evt.get_cfg_status().clone(),
367                 }))
368             }
369             TestResponseChild::TestPeriodicTxRsp(evt) => {
370                 Ok(UciResponse::RfTest(status_code_to_result(evt.get_status())))
371             }
372             TestResponseChild::StopRfTestRsp(evt) => {
373                 Ok(UciResponse::RfTest(status_code_to_result(evt.get_status())))
374             }
375             _ => Err(Error::Unknown),
376         }
377     }
378 }
379 
raw_response(evt: uwb_uci_packets::UciResponse) -> Result<UciResponse>380 fn raw_response(evt: uwb_uci_packets::UciResponse) -> Result<UciResponse> {
381     let gid: u32 = evt.get_group_id().into();
382     let oid: u32 = evt.get_opcode().into();
383     let packet: UciControlPacket = evt.into();
384     Ok(UciResponse::RawUciCmd(Ok(RawUciMessage { gid, oid, payload: packet.to_raw_payload() })))
385 }
386 
387 #[cfg(test)]
388 mod tests {
389     use super::*;
390 
391     #[test]
test_uci_response_casting_from_uci_vendor_response_packet()392     fn test_uci_response_casting_from_uci_vendor_response_packet() {
393         let mut uci_vendor_rsp_packet = uwb_uci_packets::UciResponse::try_from(
394             uwb_uci_packets::UciVendor_9_ResponseBuilder {
395                 opcode: 0x00,
396                 payload: Some(vec![0x0, 0x1, 0x2, 0x3].into()),
397             }
398             .build(),
399         )
400         .unwrap();
401         let uci_fira_major_version = UCIMajorVersion::V1;
402         let mut uci_response = UciResponse::try_from((
403             uci_vendor_rsp_packet.clone(),
404             uci_fira_major_version.clone(),
405             false,
406         ))
407         .unwrap();
408         assert_eq!(
409             uci_response,
410             UciResponse::RawUciCmd(Ok(RawUciMessage {
411                 gid: 0x9,
412                 oid: 0x0,
413                 payload: vec![0x0, 0x1, 0x2, 0x3],
414             }))
415         );
416 
417         uci_vendor_rsp_packet = uwb_uci_packets::UciResponse::try_from(
418             uwb_uci_packets::UciVendor_A_ResponseBuilder {
419                 opcode: 0x00,
420                 payload: Some(vec![0x0, 0x1, 0x2, 0x3].into()),
421             }
422             .build(),
423         )
424         .unwrap();
425         uci_response = UciResponse::try_from((
426             uci_vendor_rsp_packet.clone(),
427             uci_fira_major_version.clone(),
428             false,
429         ))
430         .unwrap();
431         assert_eq!(
432             uci_response,
433             UciResponse::RawUciCmd(Ok(RawUciMessage {
434                 gid: 0xA,
435                 oid: 0x0,
436                 payload: vec![0x0, 0x1, 0x2, 0x3],
437             }))
438         );
439 
440         uci_vendor_rsp_packet = uwb_uci_packets::UciResponse::try_from(
441             uwb_uci_packets::UciVendor_B_ResponseBuilder {
442                 opcode: 0x00,
443                 payload: Some(vec![0x0, 0x1, 0x2, 0x3].into()),
444             }
445             .build(),
446         )
447         .unwrap();
448         uci_response = UciResponse::try_from((
449             uci_vendor_rsp_packet.clone(),
450             uci_fira_major_version.clone(),
451             false,
452         ))
453         .unwrap();
454         assert_eq!(
455             uci_response,
456             UciResponse::RawUciCmd(Ok(RawUciMessage {
457                 gid: 0xB,
458                 oid: 0x0,
459                 payload: vec![0x0, 0x1, 0x2, 0x3],
460             }))
461         );
462 
463         uci_vendor_rsp_packet = uwb_uci_packets::UciResponse::try_from(
464             uwb_uci_packets::UciVendor_E_ResponseBuilder {
465                 opcode: 0x00,
466                 payload: Some(vec![0x0, 0x1, 0x2, 0x3].into()),
467             }
468             .build(),
469         )
470         .unwrap();
471         uci_response = UciResponse::try_from((
472             uci_vendor_rsp_packet.clone(),
473             uci_fira_major_version.clone(),
474             false,
475         ))
476         .unwrap();
477         assert_eq!(
478             uci_response,
479             UciResponse::RawUciCmd(Ok(RawUciMessage {
480                 gid: 0xE,
481                 oid: 0x0,
482                 payload: vec![0x0, 0x1, 0x2, 0x3],
483             }))
484         );
485 
486         uci_vendor_rsp_packet = uwb_uci_packets::UciResponse::try_from(
487             uwb_uci_packets::UciVendor_F_ResponseBuilder {
488                 opcode: 0x00,
489                 payload: Some(vec![0x0, 0x1, 0x2, 0x3].into()),
490             }
491             .build(),
492         )
493         .unwrap();
494         uci_response = UciResponse::try_from((
495             uci_vendor_rsp_packet.clone(),
496             uci_fira_major_version.clone(),
497             false,
498         ))
499         .unwrap();
500         assert_eq!(
501             uci_response,
502             UciResponse::RawUciCmd(Ok(RawUciMessage {
503                 gid: 0xF,
504                 oid: 0x0,
505                 payload: vec![0x0, 0x1, 0x2, 0x3],
506             }))
507         );
508     }
509 }
510