1# Copyright 2023 Google LLC
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#     https://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"""Client Class to access the Floss GATT client interface."""
15
16import logging
17
18from floss.pandora.floss import floss_enums
19from floss.pandora.floss import observer_base
20from floss.pandora.floss import utils
21
22
23class GattClientCallbacks:
24    """Callbacks for the GATT client interface.
25
26    Implement this to observe these callbacks when exporting callbacks via register_client.
27    """
28
29    def on_client_registered(self, status, scanner_id):
30        """Called when GATT client registered.
31
32        Args:
33            status: floss_enums.GattStatus.
34            scanner_id: Bluetooth GATT scanner id.
35        """
36        pass
37
38    def on_client_connection_state(self, status, client_id, connected, addr):
39        """Called when GATT client connection state changed.
40
41        Args:
42            status: floss_enums.GattStatus.
43            client_id: Bluetooth GATT client id.
44            connected: A boolean value representing whether the device is connected.
45            addr: Remote device MAC address.
46        """
47        pass
48
49    def on_phy_update(self, addr, tx_phy, rx_phy, status):
50        """Called when GATT physical type is updated.
51
52        Args:
53            addr: Remote device MAC address.
54            tx_phy: Transmit physical type.
55            rx_phy: Receive physical type.
56            status: floss_enums.GattStatus.
57        """
58        pass
59
60    def on_phy_read(self, addr, tx_phy, rx_phy, status):
61        """Called when GATT physical type is read.
62
63        Args:
64            addr: Remote device MAC address.
65            tx_phy: Transmit physical type.
66            rx_phy: Receive physical type.
67            status: floss_enums.GattStatus.
68        """
69        pass
70
71    def on_search_complete(self, addr, services, status):
72        """Called when search completed.
73
74        Args:
75            addr: Remote device MAC address.
76            services: Bluetooth GATT services as list.
77            status: floss_enums.GattStatus.
78        """
79        pass
80
81    def on_characteristic_read(self, addr, status, handle, value):
82        """Called when characteristic is read.
83
84        Args:
85            addr: Remote device MAC address.
86            status: floss_enums.GattStatus.
87            handle: Characteristic handle id.
88            value: Characteristic value.
89        """
90        pass
91
92    def on_characteristic_write(self, addr, status, handle):
93        """Called when characteristic is written.
94
95        Args:
96            addr: Remote device MAC address.
97            status: floss_enums.GattStatus.
98            handle: Characteristic handle id.
99        """
100        pass
101
102    def on_execute_write(self, addr, status):
103        """Called when execute write.
104
105        Args:
106            addr: Remote device MAC address.
107            status: floss_enums.GattStatus.
108        """
109        pass
110
111    def on_descriptor_read(self, addr, status, handle, value):
112        """Called when descriptor is read.
113
114        Args:
115            addr: Remote device MAC address.
116            status: floss_enums.GattStatus.
117            handle: Descriptor handle id.
118            value: Descriptor value.
119        """
120        pass
121
122    def on_descriptor_write(self, addr, status, handle):
123        """Called when descriptor is written.
124
125        Args:
126            addr: Remote device MAC address.
127            status: floss_enums.GattStatus.
128            handle: Descriptor handle id.
129        """
130        pass
131
132    def on_notify(self, addr, handle, value):
133        """Called when notified.
134
135        Args:
136            addr: Remote device MAC address.
137            handle: Characteristic handle id.
138            value: Characteristic value.
139        """
140        pass
141
142    def on_read_remote_rssi(self, addr, rssi, status):
143        """Called when remote RSSI is read.
144
145        Args:
146            addr: Remote device MAC address.
147            rssi: RSSI value.
148            status: floss_enums.GattStatus.
149        """
150        pass
151
152    def on_configure_mtu(self, addr, mtu, status):
153        """Called when MTU is configured.
154
155        Args:
156            addr: Remote device MAC address.
157            mtu: MTU value.
158            status: floss_enums.GattStatus.
159        """
160        pass
161
162    def on_connection_updated(self, addr, interval, latency, timeout, status):
163        """Called when connection updated.
164
165        Args:
166            addr: Remote device MAC address.
167            interval: Interval in ms.
168            latency: Latency in ms.
169            timeout: Timeout in ms.
170            status: floss_enums.GattStatus.
171        """
172        pass
173
174    def on_service_changed(self, addr):
175        """Called when service changed.
176
177        Args:
178            addr: Remote device MAC address.
179        """
180        pass
181
182
183class FlossGattClient(GattClientCallbacks):
184    """Handles method calls and callbacks from the GATT client interface."""
185
186    ADAPTER_SERVICE = 'org.chromium.bluetooth'
187    GATT_CLIENT_INTERFACE = 'org.chromium.bluetooth.BluetoothGatt'
188    GATT_OBJECT_PATTERN = '/org/chromium/bluetooth/hci{}/gatt'
189    GATT_CB_OBJ_NAME = 'test_gatt_client'
190    CB_EXPORTED_INTF = 'org.chromium.bluetooth.BluetoothGattCallback'
191    FLOSS_RESPONSE_LATENCY_SECS = 3
192
193    class ExportedGattClientCallbacks(observer_base.ObserverBase):
194        """
195        <node>
196            <interface name="org.chromium.bluetooth.BluetoothGattCallback">
197                <method name="OnClientRegistered">
198                    <arg type="u" name="status" direction="in" />
199                    <arg type="i" name="scanner_id" direction="in" />
200                </method>
201                <method name="OnClientConnectionState">
202                    <arg type="u" name="status" direction="in" />
203                    <arg type="i" name="client_id" direction="in" />
204                    <arg type="b" name="connected" direction="in" />
205                    <arg type="s" name="addr" direction="in" />
206                </method>
207                <method name="OnPhyUpdate">
208                    <arg type="s" name="addr" direction="in" />
209                    <arg type="u" name="tx_phy" direction="in" />
210                    <arg type="u" name="rx_phy" direction="in" />
211                    <arg type="u" name="status" direction="in" />
212                </method>
213                <method name="OnPhyRead">
214                    <arg type="s" name="addr" direction="in" />
215                    <arg type="u" name="tx_phy" direction="in" />
216                    <arg type="u" name="rx_phy" direction="in" />
217                    <arg type="u" name="status" direction="in" />
218                </method>
219                <method name="OnSearchComplete">
220                    <arg type="s" name="addr" direction="in" />
221                    <arg type="aa{sv}" name="services" direction="in" />
222                    <arg type="u" name="status" direction="in" />
223                </method>
224                <method name="OnCharacteristicRead">
225                    <arg type="s" name="addr" direction="in" />
226                    <arg type="u" name="status" direction="in" />
227                    <arg type="i" name="handle" direction="in" />
228                    <arg type="ay" name="value" direction="in" />
229                </method>
230                <method name="OnCharacteristicWrite">
231                    <arg type="s" name="addr" direction="in" />
232                    <arg type="u" name="status" direction="in" />
233                    <arg type="i" name="handle" direction="in" />
234                </method>
235                <method name="OnExecuteWrite">
236                    <arg type="s" name="addr" direction="in" />
237                    <arg type="u" name="status" direction="in" />
238                </method>
239                <method name="OnDescriptorRead">
240                    <arg type="s" name="addr" direction="in" />
241                    <arg type="u" name="status" direction="in" />
242                    <arg type="i" name="handle" direction="in" />
243                    <arg type="ay" name="value" direction="in" />
244                </method>
245                <method name="OnDescriptorWrite">
246                    <arg type="s" name="addr" direction="in" />
247                    <arg type="u" name="status" direction="in" />
248                    <arg type="i" name="handle" direction="in" />
249                </method>
250                <method name="OnNotify">
251                    <arg type="s" name="addr" direction="in" />
252                    <arg type="i" name="handle" direction="in" />
253                    <arg type="ay" name="value" direction="in" />
254                </method>
255                <method name="OnReadRemoteRssi">
256                    <arg type="s" name="addr" direction="in" />
257                    <arg type="i" name="rssi" direction="in" />
258                    <arg type="u" name="status" direction="in" />
259                </method>
260                <method name="OnConfigureMtu">
261                    <arg type="s" name="addr" direction="in" />
262                    <arg type="i" name="mtu" direction="in" />
263                    <arg type="u" name="status" direction="in" />
264                </method>
265                <method name="OnConnectionUpdated">
266                    <arg type="s" name="addr" direction="in" />
267                    <arg type="i" name="interval" direction="in" />
268                    <arg type="i" name="latency" direction="in" />
269                    <arg type="i" name="timeout" direction="in" />
270                    <arg type="u" name="status" direction="in" />
271                </method>
272                <method name="OnServiceChanged">
273                    <arg type="s" name="addr" direction="in" />
274                </method>
275
276            </interface>
277        </node>
278        """
279
280        def __init__(self):
281            """Constructs exported callbacks object."""
282            observer_base.ObserverBase.__init__(self)
283
284        def OnClientRegistered(self, status, scanner_id):
285            """Handles client registration callback.
286
287            Args:
288                status: floss_enums.GattStatus.
289                scanner_id: Bluetooth GATT scanner id.
290            """
291            for observer in self.observers.values():
292                observer.on_client_registered(status, scanner_id)
293
294        def OnClientConnectionState(self, status, client_id, connected, addr):
295            """Handles client connection state callback.
296
297            Args:
298                status: floss_enums.GattStatus.
299                client_id: Bluetooth GATT client id.
300                connected: A boolean value representing whether the device is connected.
301                addr: Remote device MAC address.
302            """
303            for observer in self.observers.values():
304                observer.on_client_connection_state(status, client_id, connected, addr)
305
306        def OnPhyUpdate(self, addr, tx_phy, rx_phy, status):
307            """Handles GATT physical type update callback.
308
309            Args:
310                addr: Remote device MAC address.
311                tx_phy: Transmit physical type.
312                rx_phy: Receive physical type.
313                status: floss_enums.GattStatus.
314            """
315            for observer in self.observers.values():
316                observer.on_phy_update(addr, tx_phy, rx_phy, status)
317
318        def OnPhyRead(self, addr, tx_phy, rx_phy, status):
319            """Handles GATT physical type read callback.
320
321            Args:
322                addr: Remote device MAC address.
323                tx_phy: Transmit physical type.
324                rx_phy: Receive physical type.
325                status: floss_enums.GattStatus.
326            """
327            for observer in self.observers.values():
328                observer.on_phy_read(addr, tx_phy, rx_phy, status)
329
330        def OnSearchComplete(self, addr, services, status):
331            """Handles search complete callback.
332
333            Args:
334                addr: Remote device MAC address.
335                services: Bluetooth GATT services as list.
336                status: floss_enums.GattStatus.
337            """
338            for observer in self.observers.values():
339                observer.on_search_complete(addr, services, status)
340
341        def OnCharacteristicRead(self, addr, status, handle, value):
342            """Handles characteristic read callback.
343
344            Args:
345                addr: Remote device MAC address.
346                status: floss_enums.GattStatus.
347                handle: Characteristic handle id.
348                value: Characteristic value.
349            """
350            for observer in self.observers.values():
351                observer.on_characteristic_read(addr, status, handle, value)
352
353        def OnCharacteristicWrite(self, addr, status, handle):
354            """Handles characteristic write callback.
355
356            Args:
357                addr: Remote device MAC address.
358                status: floss_enums.GattStatus.
359                handle: Characteristic handle id.
360            """
361            for observer in self.observers.values():
362                observer.on_characteristic_write(addr, status, handle)
363
364        def OnExecuteWrite(self, addr, status):
365            """Handles write execution callbacks.
366
367            Args:
368                addr: Remote device MAC address.
369                status: floss_enums.GattStatus.
370            """
371            for observer in self.observers.values():
372                observer.on_execute_write(addr, status)
373
374        def OnDescriptorRead(self, addr, status, handle, value):
375            """Handles descriptor read callback.
376
377            Args:
378                addr: Remote device MAC address.
379                status: floss_enums.GattStatus.
380                handle: Descriptor handle id.
381                value: Descriptor value.
382            """
383            for observer in self.observers.values():
384                observer.on_descriptor_read(addr, status, handle, value)
385
386        def OnDescriptorWrite(self, addr, status, handle):
387            """Handles descriptor write callback.
388
389            Args:
390                addr: Remote device MAC address.
391                status: floss_enums.GattStatus.
392                handle: Descriptor handle id.
393            """
394            for observer in self.observers.values():
395                observer.on_descriptor_write(addr, status, handle)
396
397        def OnNotify(self, addr, handle, value):
398            """Handles notification callback.
399
400            Args:
401                addr: Remote device MAC address.
402                handle: Characteristic handle id.
403                value: Characteristic value.
404            """
405            for observer in self.observers.values():
406                observer.on_notify(addr, handle, value)
407
408        def OnReadRemoteRssi(self, addr, rssi, status):
409            """Handles remote RSSI value read callback.
410
411            Args:
412                addr: Remote device MAC address.
413                rssi: RSSI value.
414                status: floss_enums.GattStatus.
415            """
416            for observer in self.observers.values():
417                observer.on_read_remote_rssi(addr, rssi, status)
418
419        def OnConfigureMtu(self, addr, mtu, status):
420            """Handles MTU configuration callback.
421
422            Args:
423                addr: Remote device MAC address.
424                mtu: MTU value.
425                status: floss_enums.GattStatus.
426            """
427            for observer in self.observers.values():
428                observer.on_configure_mtu(addr, mtu, status)
429
430        def OnConnectionUpdated(self, addr, interval, latency, timeout, status):
431            """Handles connection update callback.
432
433            Args:
434                addr: Remote device MAC address.
435                interval: Interval in ms.
436                latency: Latency in ms.
437                timeout: Timeout in ms.
438                status: floss_enums.GattStatus.
439            """
440            for observer in self.observers.values():
441                observer.on_connection_updated(addr, interval, latency, timeout, status)
442
443        def OnServiceChanged(self, addr):
444            """Handles service changed callback.
445
446            Args:
447                addr: Remote device MAC address.
448            """
449            for observer in self.observers.values():
450                observer.on_service_changed(addr)
451
452    def __init__(self, bus, hci):
453        """Constructs the client.
454
455        Args:
456            bus: D-Bus bus over which we'll establish connections.
457            hci: HCI adapter index. Get this value from `get_default_adapter` on FlossManagerClient.
458        """
459
460        self.bus = bus
461        self.hci = hci
462        self.callbacks = None
463        self.callback_id = None
464        self.objpath = self.GATT_OBJECT_PATTERN.format(hci)
465        self.client_id = None
466        self.gatt_services = {}
467        self.connected_clients = {}
468
469    def __del__(self):
470        """Destructor."""
471        del self.callbacks
472
473    @utils.glib_callback()
474    def on_client_registered(self, status, scanner_id):
475        """Handles client registration callback.
476
477        Args:
478            status: floss_enums.GattStatus.
479            scanner_id: Bluetooth GATT scanner id.
480        """
481        logging.debug('on_client_registered: status: %s, scanner_id: %s', status, scanner_id)
482
483        if status != floss_enums.GattStatus.SUCCESS:
484            logging.error('Failed to register client with id: %s, status = %s', scanner_id, status)
485            return
486        self.client_id = scanner_id
487
488    @utils.glib_callback()
489    def on_client_connection_state(self, status, client_id, connected, addr):
490        """Handles client connection state callback.
491
492        Args:
493            status: floss_enums.GattStatus.
494            client_id: Bluetooth GATT client id.
495            connected: A boolean value representing whether the device is connected.
496            addr: Remote device MAC address.
497        """
498        logging.debug('on_client_connection_state: status: %s, client_id: %s, '
499                      'connected: %s, addr: %s', status, client_id, connected, addr)
500        if status != floss_enums.GattStatus.SUCCESS:
501            return
502        self.connected_clients[addr] = connected
503
504    @utils.glib_callback()
505    def on_phy_update(self, addr, tx_phy, rx_phy, status):
506        """Handles physical type update callback.
507
508        Args:
509            addr: Remote device MAC address.
510            tx_phy: Transmit physical type.
511            rx_phy: Receive physical type.
512            status: floss_enums.GattStatus.
513        """
514        logging.debug('on_phy_update: addr: %s, tx_phy: %s, rx_phy: %s, status: %s', addr, tx_phy, rx_phy, status)
515
516    @utils.glib_callback()
517    def on_phy_read(self, addr, tx_phy, rx_phy, status):
518        """Handles physical type read callback.
519
520        Args:
521            addr: Remote device MAC address.
522            tx_phy: Transmit physical type.
523            rx_phy: Receive physical type.
524            status: floss_enums.GattStatus.
525        """
526        logging.debug('on_phy_read: addr: %s, tx_phy: %s, rx_phy: %s, status: %s', addr, tx_phy, rx_phy, status)
527
528    @utils.glib_callback()
529    def on_search_complete(self, addr, services, status):
530        """Handles search complete callback.
531
532        Args:
533            addr: Remote device MAC address.
534            services: Bluetooth GATT services as list.
535            status: floss_enums.GattStatus.
536        """
537        logging.debug('on_search_complete: addr: %s, services: %s, status: %s', addr, services, status)
538        if status != floss_enums.GattStatus.SUCCESS:
539            logging.error('Failed to complete search')
540            return
541        self.gatt_services[addr] = services
542
543    @utils.glib_callback()
544    def on_characteristic_read(self, addr, status, handle, value):
545        """Handles characteristic read callback.
546
547        Args:
548            addr: Remote device MAC address.
549            status: floss_enums.GattStatus.
550            handle: Characteristic handle id.
551            value: Characteristic value.
552        """
553        logging.debug('on_characteristic_read: addr: %s, status: %s, handle: %s, '
554                      'value: %s', addr, status, handle, value)
555
556    @utils.glib_callback()
557    def on_characteristic_write(self, addr, status, handle):
558        """Handles characteristic write callback.
559
560        Args:
561            addr: Remote device MAC address.
562            status: floss_enums.GattStatus.
563            handle: Characteristic handle id.
564        """
565        logging.debug('on_characteristic_write: addr: %s, status: %s, handle: %s', addr, status, handle)
566
567    @utils.glib_callback()
568    def on_execute_write(self, addr, status):
569        """Handles write execution callbacks.
570
571        Args:
572            addr: Remote device MAC address.
573            status: floss_enums.GattStatus.
574        """
575        logging.debug('on_execute_write: addr: %s, status: %s', addr, status)
576
577    @utils.glib_callback()
578    def on_descriptor_read(self, addr, status, handle, value):
579        """Handles descriptor read callback.
580
581        Args:
582            addr: Remote device MAC address.
583            status: floss_enums.GattStatus.
584            handle: Descriptor handle id.
585            value: Descriptor value.
586        """
587        logging.debug('on_descriptor_read: addr: %s, status: %s, handle: %s, value: %s', addr, status, handle, value)
588
589    @utils.glib_callback()
590    def on_descriptor_write(self, addr, status, handle):
591        """Handles descriptor write callback.
592
593       Args:
594            addr: Remote device MAC address.
595            status: floss_enums.GattStatus.
596            handle: Descriptor handle id.
597        """
598        logging.debug('on_descriptor_write: addr: %s, status: %s, handle: %s', addr, status, handle)
599
600    @utils.glib_callback()
601    def on_notify(self, addr, handle, value):
602        """Handles notification callback.
603
604        Args:
605            addr: Remote device MAC address.
606            handle: Characteristic handle id.
607            value: Characteristic value.
608        """
609        logging.debug('on_notify: addr: %s, handle: %s, value: %s', addr, handle, value)
610
611    @utils.glib_callback()
612    def on_read_remote_rssi(self, addr, rssi, status):
613        """Handles remote RSSI value read callback.
614
615        Args:
616            addr: Remote device MAC address.
617            rssi: RSSI value.
618            status: floss_enums.GattStatus.
619        """
620        logging.debug('on_read_remote_rssi: addr: %s, rssi: %s, status: %s', addr, rssi, status)
621
622    @utils.glib_callback()
623    def on_configure_mtu(self, addr, mtu, status):
624        """Handles MTU configuration callback.
625
626        Args:
627            addr: Remote device MAC address.
628            mtu: MTU value.
629            status: floss_enums.GattStatus.
630        """
631        logging.debug('on_configure_mtu: addr: %s, mtu: %s, status: %s', addr, mtu, status)
632
633    @utils.glib_callback()
634    def on_connection_updated(self, addr, interval, latency, timeout, status):
635        """Handles connection update callback.
636
637        Args:
638            addr: Remote device MAC address.
639            interval: Interval in ms.
640            latency: Latency in ms.
641            timeout: Timeout in ms.
642            status: floss_enums.GattStatus.
643        """
644        logging.debug('on_connection_updated: addr: %s, interval: %s, latency: %s, '
645                      'timeout: %s, status: %s', addr, interval, latency, timeout, status)
646
647    @utils.glib_callback()
648    def on_service_changed(self, addr):
649        """Handles service changed callback.
650
651        Args:
652            addr: Remote device MAC address.
653        """
654        logging.debug('on_service_changed: addr: %s', addr)
655
656    @utils.glib_call(False)
657    def has_proxy(self):
658        """Checks whether GATT Client can be acquired."""
659        return bool(self.proxy())
660
661    def proxy(self):
662        """Gets proxy object to GATT Client interface for method calls."""
663        return self.bus.get(self.ADAPTER_SERVICE, self.objpath)[self.GATT_CLIENT_INTERFACE]
664
665    @utils.glib_call(False)
666    def register_client(self, app_uuid, eatt_support):
667        """Registers GATT client callbacks if one doesn't already exist.
668
669        Args:
670            app_uuid: GATT application uuid.
671            eatt_support: A boolean value that indicates whether eatt is supported.
672
673        Returns:
674            True on success, False otherwise.
675        """
676        # Callbacks already registered
677        if self.callbacks:
678            return True
679        # Create and publish callbacks
680        self.callbacks = self.ExportedGattClientCallbacks()
681        self.callbacks.add_observer('gatt_testing_client', self)
682        objpath = utils.generate_dbus_cb_objpath(self.GATT_CB_OBJ_NAME, self.hci)
683        self.bus.register_object(objpath, self.callbacks, None)
684        # Register published callbacks with adapter daemon
685        self.callback_id = self.proxy().RegisterClient(app_uuid, objpath, eatt_support)
686        return True
687
688    @utils.glib_call(False)
689    def unregister_client(self):
690        """Unregisters GATT client.
691
692        Returns:
693            True on success, False otherwise.
694        """
695        self.proxy().UnregisterClient(self.client_id)
696        return True
697
698    def register_callback_observer(self, name, observer):
699        """Add an observer for all callbacks.
700
701        Args:
702            name: Name of the observer.
703            observer: Observer that implements all callback classes.
704        """
705        if isinstance(observer, GattClientCallbacks):
706            self.callbacks.add_observer(name, observer)
707
708    def unregister_callback_observer(self, name, observer):
709        """Remove an observer for all callbacks.
710
711        Args:
712            name: Name of the observer.
713            observer: Observer that implements all callback classes.
714        """
715        if isinstance(observer, GattClientCallbacks):
716            self.callbacks.remove_observer(name, observer)
717
718    @utils.glib_call(False)
719    def connect_client(self,
720                       address,
721                       is_direct=False,
722                       transport=floss_enums.BtTransport.LE,
723                       opportunistic=False,
724                       phy=floss_enums.LePhy.PHY1M):
725        """Connects GATT client.
726
727        Args:
728            address: Remote device MAC address.
729            is_direct: A boolean value represent direct status.
730            transport: floss_enums.BtTransport type.
731            opportunistic: A boolean value represent opportunistic status.
732            phy: floss_enums.LePhy type.
733
734        Returns:
735            True on success, False otherwise.
736        """
737        self.proxy().ClientConnect(self.client_id, address, is_direct, transport, opportunistic, phy)
738        return True
739
740    @utils.glib_call(False)
741    def disconnect_client(self, address):
742        """Disconnects GATT client.
743
744        Args:
745            address: Remote device MAC address.
746
747        Returns:
748            True on success, False otherwise.
749        """
750        self.proxy().ClientDisconnect(self.client_id, address)
751        return True
752
753    @utils.glib_call(False)
754    def refresh_device(self, address):
755        """Refreshes device.
756
757        Args:
758            address: Remote device MAC address.
759
760        Returns:
761            True on success, False otherwise.
762        """
763        self.proxy().RefreshDevice(self.client_id, address)
764        return True
765
766    @utils.glib_call(False)
767    def discover_services(self, address):
768        """Discovers remote device GATT services.
769
770        Args:
771            address: Remote device MAC address.
772
773        Returns:
774            True on success, False otherwise.
775        """
776        self.proxy().DiscoverServices(self.client_id, address)
777        return True
778
779    @utils.glib_call(False)
780    def discover_service_by_uuid(self, address, uuid):
781        """Discovers remote device GATT services by UUID.
782
783        Args:
784            address: Remote device MAC address.
785            uuid: The service UUID as a string.
786
787        Returns:
788            True on success, False otherwise.
789        """
790        self.proxy().DiscoverServiceByUuid(self.client_id, address, uuid)
791        return True
792
793    @utils.glib_call(False)
794    def btif_gattc_discover_service_by_uuid(self, address, uuid):
795        """Discovers remote device GATT services by UUID from btif layer.
796
797        Args:
798            address: Remote device MAC address.
799            uuid: The service UUID as a string.
800
801        Returns:
802            True on success, False otherwise.
803        """
804        self.proxy().BtifGattcDiscoverServiceByUuid(self.client_id, address, uuid)
805        return True
806
807    @utils.glib_call(False)
808    def read_characteristic(self, address, handle, auth_req):
809        """Reads GATT characteristic.
810
811        Args:
812            address: Remote device MAC address.
813            handle: Characteristic handle id.
814            auth_req: Authentication requirements value.
815
816        Returns:
817            True on success, False otherwise.
818        """
819        self.proxy().ReadCharacteristic(self.client_id, address, handle, auth_req)
820        return True
821
822    @utils.glib_call(False)
823    def read_using_characteristic_uuid(self, address, uuid, start_handle, end_handle, auth_req):
824        """Reads remote device GATT characteristic by UUID.
825
826        Args:
827            address: Remote device MAC address.
828            uuid: The characteristic UUID as a string.
829            start_handle: Characteristic start handle id.
830            end_handle: Characteristic end handle id.
831            auth_req: Authentication requirements value.
832
833        Returns:
834            True on success, False otherwise.
835        """
836        self.proxy().ReadUsingCharacteristicUuid(self.client_id, address, uuid, start_handle, end_handle, auth_req)
837        return True
838
839    @utils.glib_call(False)
840    def read_descriptor(self, address, handle, auth_req):
841        """Reads remote device GATT descriptor.
842
843        Args:
844            address: Remote device MAC address.
845            handle: Descriptor handle id.
846            auth_req: Authentication requirements value.
847
848        Returns:
849            True on success, False otherwise.
850        """
851        self.proxy().ReadDescriptor(self.client_id, address, handle, auth_req)
852        return True
853
854    @utils.glib_call(False)
855    def write_descriptor(self, address, handle, auth_req, value):
856        """Writes remote device GATT descriptor.
857
858        Args:
859            address: Remote device MAC address.
860            handle: Descriptor handle id.
861            auth_req: Authentication requirements value.
862            value: Descriptor value to write.
863
864        Returns:
865            True on success, False otherwise.
866        """
867        self.proxy().WriteDescriptor(self.client_id, address, handle, auth_req, value)
868        return True
869
870    @utils.glib_call(None)
871    def write_characteristic(self, address, handle, write_type, auth_req, value):
872        """Writes remote device GATT characteristic.
873
874        Args:
875            address: Remote device MAC address.
876            handle: Characteristic handle id.
877            write_type: Characteristic write type.
878            auth_req: Authentication requirements value.
879            value: Characteristic value to write.
880
881        Returns:
882            GattWriteRequestStatus on success, None otherwise.
883        """
884        return self.proxy().WriteCharacteristic(self.client_id, address, handle, write_type, auth_req, value)
885
886    @utils.glib_call(False)
887    def register_for_notification(self, address, handle, enable):
888        """Registers for notification.
889
890        Args:
891            address: Remote device MAC address.
892            handle: Characteristic handle id.
893            enable: Boolean value represents enabling or disabling notify.
894
895        Returns:
896            True on success, False otherwise.
897        """
898        self.proxy().RegisterForNotification(self.client_id, address, handle, enable)
899        return True
900
901    @utils.glib_call(False)
902    def begin_reliable_write(self, address):
903        """Begins a reliable write transaction.
904
905        Args:
906            address: Remote device MAC address.
907
908        Returns:
909            True on success, False otherwise.
910        """
911        self.proxy().BeginReliableWrite(self.client_id, address)
912        return True
913
914    @utils.glib_call(False)
915    def end_reliable_write(self, address, execute):
916        """Ends the reliable write transaction.
917
918        Args:
919            address: Remote device MAC address.
920            execute: Boolean to execute or not.
921
922        Returns:
923            True on success, False otherwise.
924        """
925        self.proxy().EndReliableWrite(self.client_id, address, execute)
926        return True
927
928    @utils.glib_call(False)
929    def read_remote_rssi(self, address):
930        """Reads remote device RSSI.
931
932        Args:
933            address: Remote device MAC address.
934
935        Returns:
936            True on success, False otherwise.
937        """
938        self.proxy().ReadRemoteRssi(self.client_id, address)
939        return True
940
941    @utils.glib_call(False)
942    def configure_mtu(self, address, mtu):
943        """Configures MTU value.
944
945        Args:
946            address: Remote device MAC address.
947            mtu: MTU value.
948
949        Returns:
950            True on success, False otherwise.
951        """
952        self.proxy().ConfigureMtu(self.client_id, address, mtu)
953        return True
954
955    @utils.glib_call(False)
956    def update_connection_parameter(self, address, min_interval, max_interval, latency, timeout, min_ce_len,
957                                    max_ce_len):
958        """Updates connection parameters.
959
960        Args:
961            address: Remote device MAC address.
962            min_interval: Minimum interval in ms.
963            max_interval: Maximum interval in ms.
964            latency: Latency interval in ms.
965            timeout: Timeout interval in ms.
966            min_ce_len: Connection event minimum length in ms.
967            max_ce_len: Connection event maximum length in ms.
968
969        Returns:
970            True on success, False otherwise.
971        """
972        self.proxy().ConnectionParameterUpdate(self.client_id, address, min_interval, max_interval, latency, timeout,
973                                               min_ce_len, max_ce_len)
974        return True
975
976    @utils.glib_call(False)
977    def set_preferred_phy(self, address, tx_phy, rx_phy, phy_options):
978        """Sets remote device preferred physical options.
979
980        Args:
981            address: Remote device MAC address.
982            tx_phy: Transmit physical type.
983            rx_phy: Receive physical type.
984            phy_options: Physical options to use for connection.
985
986        Returns:
987            True on success, False otherwise.
988        """
989        self.proxy().ClientSetPreferredPhy(self.client_id, address, tx_phy, rx_phy, phy_options)
990        return True
991
992    @utils.glib_call(False)
993    def read_phy(self, address):
994        """Reads remote device physical setting.
995
996        Args:
997            address: Remote device MAC address.
998
999        Returns:
1000            True on success, False otherwise.
1001        """
1002        self.proxy().ClientReadPhy(self.client_id, address)
1003        return True
1004
1005    def wait_for_client_connected(self, address):
1006        """Waits for GATT client to be connected.
1007
1008        Args:
1009            address: Remote device MAC address.
1010
1011        Returns:
1012            True on success, False otherwise.
1013        """
1014        try:
1015            utils.poll_for_condition(condition=lambda: self.connected_clients.get(address),
1016                                     timeout=self.FLOSS_RESPONSE_LATENCY_SECS)
1017            return True
1018
1019        except utils.TimeoutError:
1020            logging.error('on_client_connection_state not called')
1021            return False
1022
1023    def wait_for_search_complete(self, address):
1024        """Waits for GATT search to be completed.
1025
1026        Args:
1027            address: Remote device MAC address.
1028
1029        Returns:
1030            True on success, False otherwise.
1031        """
1032        try:
1033            utils.poll_for_condition(condition=lambda: address in self.gatt_services,
1034                                     timeout=self.FLOSS_RESPONSE_LATENCY_SECS)
1035            return True
1036
1037        except utils.TimeoutError:
1038            logging.error('on_search_complete not called')
1039            return False
1040
1041    def connect_client_sync(self, address):
1042        """Connects GATT client.
1043
1044        Args:
1045            address: Remote device MAC address.
1046
1047        Returns:
1048            Client id on success, None otherwise.
1049        """
1050        self.connect_client(address=address)
1051        if not self.wait_for_client_connected(address):
1052            return None
1053        return self.connected_clients[address]
1054
1055    def discover_services_sync(self, address):
1056        """Discovers remote device GATT services.
1057
1058        Args:
1059            address: Remote device MAC address.
1060
1061        Returns:
1062            Remote device GATT services as a list on success, None otherwise.
1063        """
1064        self.discover_services(address)
1065        if not self.wait_for_search_complete(address):
1066            return None
1067        return self.gatt_services[address]
1068
1069    def register_callback_observer(self, name, observer):
1070        """Adds an observer for all callbacks.
1071
1072        Args:
1073            name: Name of the observer.
1074            observer: Observer that implements all callback classes.
1075        """
1076        if isinstance(observer, GattClientCallbacks):
1077            self.callbacks.add_observer(name, observer)
1078
1079    def unregister_callback_observer(self, name, observer):
1080        """Removes an observer for all callbacks.
1081
1082        Args:
1083            name: Name of the observer.
1084            observer: Observer that implements all callback classes.
1085        """
1086        if isinstance(observer, GattClientCallbacks):
1087            self.callbacks.remove_observer(name, observer)
1088