• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16import inspect
17
18from acts.controllers.buds_lib.dev_utils import apollo_sink_events
19
20
21def validate_controller(controller, abstract_device_class):
22    """Ensure controller has all methods in abstract_device_class.
23    Also checks method signatures to ensure parameters are satisfied.
24
25    Args:
26        controller: instance of a device controller.
27        abstract_device_class: class definition of an abstract_device interface.
28    Raises:
29         NotImplementedError: if controller is missing one or more methods.
30    """
31    ctlr_methods = inspect.getmembers(controller, predicate=callable)
32    reqd_methods = inspect.getmembers(abstract_device_class,
33                                      predicate=inspect.ismethod)
34    expected_func_names = {method[0] for method in reqd_methods}
35    controller_func_names = {method[0] for method in ctlr_methods}
36
37    if not controller_func_names.issuperset(expected_func_names):
38        raise NotImplementedError(
39            'Controller {} is missing the following functions: {}'.format(
40                controller.__class__.__name__,
41                repr(expected_func_names - controller_func_names)))
42
43    for func_name in expected_func_names:
44        controller_func = getattr(controller, func_name)
45        required_func = getattr(abstract_device_class, func_name)
46        required_signature = inspect.signature(required_func)
47        if inspect.signature(controller_func) != required_signature:
48            raise NotImplementedError(
49                'Method {} must have the signature {}{}.'.format(
50                    controller_func.__qualname__,
51                    controller_func.__name__,
52                    required_signature))
53
54
55class BluetoothHandsfreeAbstractDevice:
56    """Base class for all Bluetooth handsfree abstract devices.
57
58    Desired controller classes should have a corresponding Bluetooth handsfree
59    abstract device class defined in this module.
60    """
61    @property
62    def mac_address(self):
63        raise NotImplementedError
64
65    def accept_call(self):
66        raise NotImplementedError()
67
68    def end_call(self):
69        raise NotImplementedError()
70
71    def enter_pairing_mode(self):
72        raise NotImplementedError()
73
74    def next_track(self):
75        raise NotImplementedError()
76
77    def pause(self):
78        raise NotImplementedError()
79
80    def play(self):
81        raise NotImplementedError()
82
83    def power_off(self):
84        raise NotImplementedError()
85
86    def power_on(self):
87        raise NotImplementedError()
88
89    def previous_track(self):
90        raise NotImplementedError()
91
92    def reject_call(self):
93        raise NotImplementedError()
94
95    def volume_down(self):
96        raise NotImplementedError()
97
98    def volume_up(self):
99        raise NotImplementedError()
100
101
102class PixelBudsBluetoothHandsfreeAbstractDevice(
103    BluetoothHandsfreeAbstractDevice):
104
105    CMD_EVENT = 'EvtHex'
106
107    def __init__(self, pixel_buds_controller):
108        self.pixel_buds_controller = pixel_buds_controller
109
110    def format_cmd(self, cmd_name):
111        return self.CMD_EVENT + ' ' + apollo_sink_events.SINK_EVENTS[cmd_name]
112
113    @property
114    def mac_address(self):
115        return self.pixel_buds_controller.bluetooth_address
116
117    def accept_call(self):
118        return self.pixel_buds_controller.cmd(self.format_cmd('EventUsrAnswer'))
119
120    def end_call(self):
121        return self.pixel_buds_controller.cmd(
122            self.format_cmd('EventUsrCancelEnd'))
123
124    def enter_pairing_mode(self):
125        return self.pixel_buds_controller.set_pairing_mode()
126
127    def next_track(self):
128        return self.pixel_buds_controller.cmd(
129            self.format_cmd('EventUsrAvrcpSkipForward'))
130
131    def pause(self):
132        return self.pixel_buds_controller.cmd(
133            self.format_cmd('EventUsrAvrcpPause'))
134
135    def play(self):
136        return self.pixel_buds_controller.cmd(
137            self.format_cmd('EventUsrAvrcpPlay'))
138
139    def power_off(self):
140        return self.pixel_buds_controller.power('Off')
141
142    def power_on(self):
143        return self.pixel_buds_controller.power('On')
144
145    def previous_track(self):
146        return self.pixel_buds_controller.cmd(
147            self.format_cmd('EventUsrAvrcpSkipBackward'))
148
149    def reject_call(self):
150        return self.pixel_buds_controller.cmd(self.format_cmd('EventUsrReject'))
151
152    def volume_down(self):
153        return self.pixel_buds_controller.volume('Down')
154
155    def volume_up(self):
156        return self.pixel_buds_controller.volume('Up')
157
158
159class EarstudioReceiverBluetoothHandsfreeAbstractDevice(
160        BluetoothHandsfreeAbstractDevice):
161
162    def __init__(self, earstudio_controller):
163        self.earstudio_controller = earstudio_controller
164
165    @property
166    def mac_address(self):
167        return self.earstudio_controller.mac_address
168
169    def accept_call(self):
170        return self.earstudio_controller.press_accept_call()
171
172    def end_call(self):
173        return self.earstudio_controller.press_end_call()
174
175    def enter_pairing_mode(self):
176        return self.earstudio_controller.enter_pairing_mode()
177
178    def next_track(self):
179        return self.earstudio_controller.press_next()
180
181    def pause(self):
182        return self.earstudio_controller.press_play_pause()
183
184    def play(self):
185        return self.earstudio_controller.press_play_pause()
186
187    def power_off(self):
188        return self.earstudio_controller.power_off()
189
190    def power_on(self):
191        return self.earstudio_controller.power_on()
192
193    def previous_track(self):
194        return self.earstudio_controller.press_previous()
195
196    def reject_call(self):
197        return self.earstudio_controller.press_reject_call()
198
199    def volume_down(self):
200        return self.earstudio_controller.press_volume_down()
201
202    def volume_up(self):
203        return self.earstudio_controller.press_volume_up()
204
205
206class JaybirdX3EarbudsBluetoothHandsfreeAbstractDevice(
207        BluetoothHandsfreeAbstractDevice):
208
209    def __init__(self, jaybird_controller):
210        self.jaybird_controller = jaybird_controller
211
212    @property
213    def mac_address(self):
214        return self.jaybird_controller.mac_address
215
216    def accept_call(self):
217        return self.jaybird_controller.press_accept_call()
218
219    def end_call(self):
220        return self.jaybird_controller.press_reject_call()
221
222    def enter_pairing_mode(self):
223        return self.jaybird_controller.enter_pairing_mode()
224
225    def next_track(self):
226        return self.jaybird_controller.press_next()
227
228    def pause(self):
229        return self.jaybird_controller.press_play_pause()
230
231    def play(self):
232        return self.jaybird_controller.press_play_pause()
233
234    def power_off(self):
235        return self.jaybird_controller.power_off()
236
237    def power_on(self):
238        return self.jaybird_controller.power_on()
239
240    def previous_track(self):
241        return self.jaybird_controller.press_previous()
242
243    def reject_call(self):
244        return self.jaybird_controller.press_reject_call()
245
246    def volume_down(self):
247        return self.jaybird_controller.press_volume_down()
248
249    def volume_up(self):
250        return self.jaybird_controller.press_volume_up()
251
252
253class BluetoothHandsfreeAbstractDeviceFactory:
254    """Generates a BluetoothHandsfreeAbstractDevice for any device controller.
255    """
256
257    _controller_abstract_devices = {
258        'EarstudioReceiver': EarstudioReceiverBluetoothHandsfreeAbstractDevice,
259        'JaybirdX3Earbuds': JaybirdX3EarbudsBluetoothHandsfreeAbstractDevice,
260        'ParentDevice': PixelBudsBluetoothHandsfreeAbstractDevice
261    }
262
263    def generate(self, controller):
264        class_name = controller.__class__.__name__
265        if class_name in self._controller_abstract_devices:
266            return self._controller_abstract_devices[class_name](controller)
267        else:
268            validate_controller(controller, BluetoothHandsfreeAbstractDevice)
269            return controller
270