1use crate::input_device::{self, InputEvent, UnhandledInputEvent};
6use crate::input_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
7use crate::keyboard_binding::{KeyboardDeviceDescriptor, KeyboardEvent};
8use crate::metrics;
9use anyhow::{Context, Result};
10use async_trait::async_trait;
11use fidl_fuchsia_ui_composition_internal as fcomp;
12use fidl_fuchsia_ui_input3::KeyEventType;
13use fuchsia_async::{OnSignals, Task};
14use fuchsia_inspect::health::Reporter;
15use futures::StreamExt;
16use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
17use keymaps::KeyState;
18use metrics_registry::InputPipelineErrorMetricDimensionEvent;
19use std::cell::RefCell;
20use std::rc::Rc;
21use std::sync::LazyLock;
22use zx::{AsHandleRef, MonotonicDuration, MonotonicInstant, Signals, Status};
23
24static DISPLAY_OWNED: LazyLock<Signals> = LazyLock::new(|| {
27 Signals::from_bits(fcomp::SIGNAL_DISPLAY_OWNED).expect("static init should not fail")
28});
29
30static DISPLAY_UNOWNED: LazyLock<Signals> = LazyLock::new(|| {
33 Signals::from_bits(fcomp::SIGNAL_DISPLAY_NOT_OWNED).expect("static init should not fail")
34});
35
36static ANY_DISPLAY_EVENT: LazyLock<Signals> = LazyLock::new(|| *DISPLAY_OWNED | *DISPLAY_UNOWNED);
38
39#[derive(Debug, Clone, PartialEq)]
41struct Ownership {
42 signals: Signals,
43}
44
45impl std::convert::From<Signals> for Ownership {
46 fn from(signals: Signals) -> Self {
47 Ownership { signals }
48 }
49}
50
51impl Ownership {
52 fn is_display_ownership_lost(&self) -> bool {
55 self.signals.contains(*DISPLAY_UNOWNED)
56 }
57
58 fn next_signal(&self) -> Signals {
62 match self.is_display_ownership_lost() {
63 true => *DISPLAY_OWNED,
64 false => *DISPLAY_UNOWNED,
65 }
66 }
67
68 async fn wait_ownership_change<'a, T: AsHandleRef>(
73 &self,
74 event: &'a T,
75 ) -> Result<Signals, Status> {
76 OnSignals::new(event, self.next_signal()).await
77 }
78}
79
80pub struct DisplayOwnership {
100 ownership: Rc<RefCell<Ownership>>,
103
104 key_state: RefCell<KeyState>,
106
107 display_ownership_change_receiver: RefCell<Option<UnboundedReceiver<Ownership>>>,
109
110 _display_ownership_task: Task<()>,
113
114 metrics_logger: metrics::MetricsLogger,
116
117 inspect_status: InputHandlerStatus,
119
120 #[cfg(test)]
126 loop_done: RefCell<Option<UnboundedSender<()>>>,
127}
128
129impl DisplayOwnership {
130 pub fn new(
137 display_ownership_event: impl AsHandleRef + 'static,
138 input_handlers_node: &fuchsia_inspect::Node,
139 metrics_logger: metrics::MetricsLogger,
140 ) -> Rc<Self> {
141 DisplayOwnership::new_internal(
142 display_ownership_event,
143 None,
144 input_handlers_node,
145 metrics_logger,
146 )
147 }
148
149 #[cfg(test)]
150 pub fn new_for_test(
151 display_ownership_event: impl AsHandleRef + 'static,
152 loop_done: UnboundedSender<()>,
153 metrics_logger: metrics::MetricsLogger,
154 ) -> Rc<Self> {
155 let inspector = fuchsia_inspect::Inspector::default();
156 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
157 DisplayOwnership::new_internal(
158 display_ownership_event,
159 Some(loop_done),
160 &fake_handlers_node,
161 metrics_logger,
162 )
163 }
164
165 fn new_internal(
166 display_ownership_event: impl AsHandleRef + 'static,
167 _loop_done: Option<UnboundedSender<()>>,
168 input_handlers_node: &fuchsia_inspect::Node,
169 metrics_logger: metrics::MetricsLogger,
170 ) -> Rc<Self> {
171 let initial_state = display_ownership_event
172 .as_handle_ref()
175 .wait_one(*ANY_DISPLAY_EVENT, MonotonicInstant::INFINITE_PAST)
176 .expect("unable to set the initial display state");
177 log::debug!("setting initial display ownership to: {:?}", &initial_state);
178 let initial_ownership: Ownership = initial_state.into();
179 let ownership = Rc::new(RefCell::new(initial_ownership.clone()));
180
181 let mut ownership_clone = initial_ownership;
182 let (ownership_sender, ownership_receiver) = mpsc::unbounded();
183 let display_ownership_task = Task::local(async move {
184 loop {
185 let signals = ownership_clone.wait_ownership_change(&display_ownership_event).await;
186 match signals {
187 Err(e) => {
188 log::warn!("could not read display state: {:?}", e);
189 break;
190 }
191 Ok(signals) => {
192 log::debug!("setting display ownership to: {:?}", &signals);
193 ownership_sender.unbounded_send(signals.into()).unwrap();
194 ownership_clone = signals.into();
195 }
196 }
197 }
198 log::warn!(
199 "display loop exiting and will no longer monitor display changes - this is not expected"
200 );
201 });
202 log::info!("Display ownership handler installed");
203 let inspect_status = InputHandlerStatus::new(
204 input_handlers_node,
205 "display_ownership",
206 false,
207 );
208 Rc::new(Self {
209 ownership,
210 key_state: RefCell::new(KeyState::new()),
211 display_ownership_change_receiver: RefCell::new(Some(ownership_receiver)),
212 _display_ownership_task: display_ownership_task,
213 metrics_logger,
214 inspect_status,
215 #[cfg(test)]
216 loop_done: RefCell::new(_loop_done),
217 })
218 }
219
220 fn is_display_ownership_lost(&self) -> bool {
222 self.ownership.borrow().is_display_ownership_lost()
223 }
224
225 pub async fn handle_ownership_change(
232 self: &Rc<Self>,
233 output: UnboundedSender<Vec<InputEvent>>,
234 ) -> Result<()> {
235 let mut ownership_source = self
236 .display_ownership_change_receiver
237 .borrow_mut()
238 .take()
239 .context("display_ownership_change_receiver already taken")?;
240 while let Some(new_ownership) = ownership_source.next().await {
241 let is_display_ownership_lost = new_ownership.is_display_ownership_lost();
242 let event_type = match is_display_ownership_lost {
246 true => KeyEventType::Cancel,
247 false => KeyEventType::Sync,
248 };
249 let keys = self.key_state.borrow().get_set();
250 let mut event_time = MonotonicInstant::get();
251 for key in keys.into_iter() {
252 let key_event = KeyboardEvent::new(key, event_type);
253 output
254 .unbounded_send(vec![into_input_event(key_event, event_time)])
255 .context("unable to send display updates")?;
256 event_time = event_time + MonotonicDuration::from_nanos(1);
257 }
258 *(self.ownership.borrow_mut()) = new_ownership;
259 #[cfg(test)]
260 {
261 if let Some(loop_done) = self.loop_done.borrow().as_ref() {
262 loop_done.unbounded_send(()).unwrap();
263 }
264 }
265 }
266 Ok(())
267 }
268}
269
270impl Handler for DisplayOwnership {
271 fn set_handler_healthy(self: std::rc::Rc<Self>) {
272 self.inspect_status.health_node.borrow_mut().set_ok();
273 }
274
275 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
276 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
277 }
278
279 fn get_name(&self) -> &'static str {
280 "DisplayOwnership"
281 }
282
283 fn interest(&self) -> Vec<input_device::InputEventType> {
284 vec![input_device::InputEventType::Keyboard]
285 }
286}
287
288#[async_trait(?Send)]
289impl UnhandledInputHandler for DisplayOwnership {
290 async fn handle_unhandled_input_event(
291 self: Rc<Self>,
292 unhandled_input_event: UnhandledInputEvent,
293 ) -> Vec<input_device::InputEvent> {
294 fuchsia_trace::duration!("input", "display_ownership");
295 self.inspect_status.count_received_event(&unhandled_input_event.event_time);
296 match unhandled_input_event.device_event {
297 input_device::InputDeviceEvent::Keyboard(ref e) => {
298 self.key_state.borrow_mut().update(e.get_event_type(), e.get_key());
299 }
300 _ => {
301 self.metrics_logger.log_error(
302 InputPipelineErrorMetricDimensionEvent::HandlerReceivedUninterestedEvent,
303 std::format!(
304 "uninterested input event: {:?}",
305 unhandled_input_event.get_event_type()
306 ),
307 );
308 }
309 }
310 let is_display_ownership_lost = self.is_display_ownership_lost();
311 if is_display_ownership_lost {
312 self.inspect_status.count_handled_event();
313 }
314
315 #[cfg(test)]
316 {
317 if let Some(loop_done) = self.loop_done.borrow().as_ref() {
318 loop_done.unbounded_send(()).unwrap();
319 }
320 }
321
322 vec![
323 input_device::InputEvent::from(unhandled_input_event)
324 .into_handled_if(is_display_ownership_lost),
325 ]
326 }
327}
328
329fn empty_keyboard_device_descriptor() -> input_device::InputDeviceDescriptor {
330 input_device::InputDeviceDescriptor::Keyboard(
331 KeyboardDeviceDescriptor {
333 keys: vec![],
334 device_information: fidl_fuchsia_input_report::DeviceInformation {
335 vendor_id: Some(0),
336 product_id: Some(0),
337 version: Some(0),
338 polling_rate: Some(0),
339 ..Default::default()
340 },
341 device_id: 0,
342 },
343 )
344}
345
346fn into_input_event(
347 keyboard_event: KeyboardEvent,
348 event_time: MonotonicInstant,
349) -> input_device::InputEvent {
350 input_device::InputEvent {
351 device_event: input_device::InputDeviceEvent::Keyboard(keyboard_event),
352 device_descriptor: empty_keyboard_device_descriptor(),
353 event_time,
354 handled: input_device::Handled::No,
355 trace_id: None,
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::testing_utilities::{create_fake_input_event, create_input_event};
363 use fidl_fuchsia_input::Key;
364 use fuchsia_async as fasync;
365 use pretty_assertions::assert_eq;
366 use std::convert::TryFrom as _;
367 use zx::{EventPair, Peered};
368
369 struct DisplayWrangler {
373 event: EventPair,
374 last: Signals,
375 }
376
377 impl DisplayWrangler {
378 fn new(event: EventPair) -> Self {
379 let mut instance = DisplayWrangler { event, last: *DISPLAY_OWNED };
380 instance.set_unowned();
384 instance
385 }
386
387 fn set_unowned(&mut self) {
388 assert!(self.last != *DISPLAY_UNOWNED, "display is already unowned");
389 self.event.signal_peer(*DISPLAY_OWNED, *DISPLAY_UNOWNED).unwrap();
390 self.last = *DISPLAY_UNOWNED;
391 }
392
393 fn set_owned(&mut self) {
394 assert!(self.last != *DISPLAY_OWNED, "display is already owned");
395 self.event.signal_peer(*DISPLAY_UNOWNED, *DISPLAY_OWNED).unwrap();
396 self.last = *DISPLAY_OWNED;
397 }
398 }
399
400 #[fuchsia::test]
401 async fn display_ownership_change() {
402 let (test_event, handler_event) = EventPair::create();
406
407 let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
409
410 let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
412
413 let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
416
417 let mut wrangler = DisplayWrangler::new(test_event);
420 let handler = DisplayOwnership::new_for_test(
421 handler_event,
422 loop_done_sender,
423 metrics::MetricsLogger::default(),
424 );
425
426 let handler_clone = handler.clone();
427 let handler_sender_clone = handler_sender.clone();
428 let _task = fasync::Task::local(async move {
429 handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
430 });
431
432 let handler_clone_2 = handler.clone();
433 let _input_task = fasync::Task::local(async move {
434 let mut receiver = handler_receiver;
435 while let Some(event) = receiver.next().await {
436 let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
437 let out_events =
438 handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
439 handler_sender.unbounded_send(out_events).unwrap();
440 }
441 });
442
443 let fake_time = MonotonicInstant::from_nanos(42);
444
445 wrangler.set_owned();
449 loop_done.next().await;
450 test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
451 loop_done.next().await;
452
453 wrangler.set_unowned();
455 loop_done.next().await;
456 test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
457 loop_done.next().await;
458
459 wrangler.set_owned();
461 loop_done.next().await;
462 test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
463 loop_done.next().await;
464
465 wrangler.set_unowned();
467 loop_done.next().await;
468 test_sender.unbounded_send(create_fake_input_event(fake_time)).unwrap();
469 loop_done.next().await;
470
471 let actual: Vec<InputEvent> = test_receiver
472 .take(4)
473 .flat_map(|events| futures::stream::iter(events))
474 .map(|e| e.into_with_event_time(fake_time))
475 .collect()
476 .await;
477
478 assert_eq!(
479 actual,
480 vec![
481 create_fake_input_event(fake_time),
483 create_fake_input_event(fake_time).into_handled(),
485 create_fake_input_event(fake_time),
487 create_fake_input_event(fake_time).into_handled(),
489 ]
490 );
491 }
492
493 fn new_keyboard_input_event(key: Key, event_type: KeyEventType) -> InputEvent {
494 let fake_time = MonotonicInstant::from_nanos(42);
495 create_input_event(
496 KeyboardEvent::new(key, event_type),
497 &input_device::InputDeviceDescriptor::Fake,
498 fake_time,
499 input_device::Handled::No,
500 )
501 }
502
503 #[fuchsia::test]
504 async fn basic_key_state_handling() {
505 let (test_event, handler_event) = EventPair::create();
506 let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
507 let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
508 let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
509 let mut wrangler = DisplayWrangler::new(test_event);
510 let handler = DisplayOwnership::new_for_test(
511 handler_event,
512 loop_done_sender,
513 metrics::MetricsLogger::default(),
514 );
515
516 let handler_clone = handler.clone();
517 let handler_sender_clone = handler_sender.clone();
518 let _task = fasync::Task::local(async move {
519 handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
520 });
521
522 let handler_clone_2 = handler.clone();
523 let _input_task = fasync::Task::local(async move {
524 let mut receiver = handler_receiver;
525 while let Some(event) = receiver.next().await {
526 let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
527 let out_events =
528 handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
529 handler_sender.unbounded_send(out_events).unwrap();
530 }
531 });
532
533 let fake_time = MonotonicInstant::from_nanos(42);
534
535 wrangler.set_owned();
537 loop_done.next().await;
538 test_sender
539 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
540 .unwrap();
541 loop_done.next().await;
542
543 wrangler.set_unowned();
545 loop_done.next().await;
546
547 wrangler.set_owned();
549 loop_done.next().await;
550
551 test_sender
553 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
554 .unwrap();
555 loop_done.next().await;
556
557 let actual: Vec<InputEvent> = test_receiver
558 .take(4)
559 .flat_map(|events| futures::stream::iter(events))
560 .map(|e| e.into_with_event_time(fake_time))
561 .collect()
562 .await;
563
564 assert_eq!(
565 actual,
566 vec![
567 new_keyboard_input_event(Key::A, KeyEventType::Pressed),
568 new_keyboard_input_event(Key::A, KeyEventType::Cancel)
569 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
570 new_keyboard_input_event(Key::A, KeyEventType::Sync)
571 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
572 new_keyboard_input_event(Key::A, KeyEventType::Released),
573 ]
574 );
575 }
576
577 #[fuchsia::test]
578 async fn more_key_state_handling() {
579 let (test_event, handler_event) = EventPair::create();
580 let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
581 let (handler_sender, test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
582 let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
583 let mut wrangler = DisplayWrangler::new(test_event);
584 let handler = DisplayOwnership::new_for_test(
585 handler_event,
586 loop_done_sender,
587 metrics::MetricsLogger::default(),
588 );
589
590 let handler_clone = handler.clone();
591 let handler_sender_clone = handler_sender.clone();
592 let _task = fasync::Task::local(async move {
593 handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
594 });
595
596 let handler_clone_2 = handler.clone();
597 let _input_task = fasync::Task::local(async move {
598 let mut receiver = handler_receiver;
599 while let Some(event) = receiver.next().await {
600 let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
601 let out_events =
602 handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
603 handler_sender.unbounded_send(out_events).unwrap();
604 }
605 });
606
607 let fake_time = MonotonicInstant::from_nanos(42);
608
609 wrangler.set_owned();
610 loop_done.next().await;
611 test_sender
612 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
613 .unwrap();
614 loop_done.next().await;
615 test_sender
616 .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Pressed))
617 .unwrap();
618 loop_done.next().await;
619
620 wrangler.set_unowned();
622 loop_done.next().await;
623 test_sender
624 .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Released))
625 .unwrap();
626 loop_done.next().await;
627 test_sender
628 .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Pressed))
629 .unwrap();
630 loop_done.next().await;
631
632 wrangler.set_owned();
634 loop_done.next().await;
635
636 test_sender
638 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
639 .unwrap();
640 loop_done.next().await;
641 test_sender
642 .unbounded_send(new_keyboard_input_event(Key::C, KeyEventType::Released))
643 .unwrap();
644 loop_done.next().await;
645
646 let actual: Vec<InputEvent> = test_receiver
647 .take(10) .flat_map(|events| futures::stream::iter(events))
649 .map(|e| e.into_with_event_time(fake_time))
650 .collect()
651 .await;
652
653 assert_eq!(
654 actual,
655 vec![
656 new_keyboard_input_event(Key::A, KeyEventType::Pressed),
657 new_keyboard_input_event(Key::B, KeyEventType::Pressed),
658 new_keyboard_input_event(Key::A, KeyEventType::Cancel)
659 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
660 new_keyboard_input_event(Key::B, KeyEventType::Cancel)
661 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
662 new_keyboard_input_event(Key::B, KeyEventType::Released).into_handled(),
663 new_keyboard_input_event(Key::C, KeyEventType::Pressed).into_handled(),
664 new_keyboard_input_event(Key::A, KeyEventType::Sync)
668 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
669 new_keyboard_input_event(Key::C, KeyEventType::Sync)
670 .into_with_device_descriptor(empty_keyboard_device_descriptor()),
671 new_keyboard_input_event(Key::A, KeyEventType::Released),
672 new_keyboard_input_event(Key::C, KeyEventType::Released),
673 ]
674 );
675 }
676
677 #[fuchsia::test]
678 async fn display_ownership_initialized_with_inspect_node() {
679 let (test_event, handler_event) = EventPair::create();
680 let (loop_done_sender, _) = mpsc::unbounded::<()>();
681 let inspector = fuchsia_inspect::Inspector::default();
682 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
683 let _ = DisplayWrangler::new(test_event);
685 let _handler = DisplayOwnership::new_internal(
686 handler_event,
687 Some(loop_done_sender),
688 &fake_handlers_node,
689 metrics::MetricsLogger::default(),
690 );
691 diagnostics_assertions::assert_data_tree!(inspector, root: {
692 input_handlers_node: {
693 display_ownership: {
694 events_received_count: 0u64,
695 events_handled_count: 0u64,
696 last_received_timestamp_ns: 0u64,
697 "fuchsia.inspect.Health": {
698 status: "STARTING_UP",
699 start_timestamp_nanos: diagnostics_assertions::AnyProperty
702 },
703 }
704 }
705 });
706 }
707
708 #[fuchsia::test]
709 async fn display_ownership_inspect_counts_events() {
710 let (test_event, handler_event) = EventPair::create();
711 let (test_sender, handler_receiver) = mpsc::unbounded::<InputEvent>();
712 let (handler_sender, _test_receiver) = mpsc::unbounded::<Vec<InputEvent>>();
713 let (loop_done_sender, mut loop_done) = mpsc::unbounded::<()>();
714 let mut wrangler = DisplayWrangler::new(test_event);
715 let inspector = fuchsia_inspect::Inspector::default();
716 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
717 let handler = DisplayOwnership::new_internal(
718 handler_event,
719 Some(loop_done_sender),
720 &fake_handlers_node,
721 metrics::MetricsLogger::default(),
722 );
723 let handler_clone = handler.clone();
724 let handler_sender_clone = handler_sender.clone();
725 let _task = fasync::Task::local(async move {
726 handler_clone.handle_ownership_change(handler_sender_clone).await.unwrap();
727 });
728
729 let handler_clone_2 = handler.clone();
730 let _input_task = fasync::Task::local(async move {
731 let mut receiver = handler_receiver;
732 while let Some(event) = receiver.next().await {
733 let unhandled_event = UnhandledInputEvent::try_from(event).unwrap();
734 let out_events =
735 handler_clone_2.clone().handle_unhandled_input_event(unhandled_event).await;
736 handler_sender.unbounded_send(out_events).unwrap();
737 }
738 });
739
740 wrangler.set_owned();
742 loop_done.next().await;
743 test_sender
744 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Pressed))
745 .unwrap();
746 loop_done.next().await;
747
748 wrangler.set_unowned();
751 loop_done.next().await;
752 test_sender
753 .unbounded_send(new_keyboard_input_event(Key::B, KeyEventType::Pressed))
754 .unwrap();
755 loop_done.next().await;
756
757 wrangler.set_owned();
759 loop_done.next().await;
760
761 test_sender
763 .unbounded_send(new_keyboard_input_event(Key::A, KeyEventType::Released))
764 .unwrap();
765 loop_done.next().await;
766
767 diagnostics_assertions::assert_data_tree!(inspector, root: {
768 input_handlers_node: {
769 display_ownership: {
770 events_received_count: 3u64,
771 events_handled_count: 1u64,
772 last_received_timestamp_ns: 42u64,
773 "fuchsia.inspect.Health": {
774 status: "STARTING_UP",
775 start_timestamp_nanos: diagnostics_assertions::AnyProperty
778 },
779 }
780 }
781 });
782 }
783}