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 telephony interface.""" 15import logging 16 17from floss.pandora.floss import observer_base 18from floss.pandora.floss import utils 19 20 21class BluetoothTelephonyCallbacks: 22 """Callbacks for the telephony interface. 23 24 Implement this to observe these callbacks when exporting callbacks via register_callback. 25 """ 26 27 def on_telephony_use(self, addr, state): 28 """Called when telephony is in use. 29 30 Args: 31 addr: The address of the telephony device. 32 state: The boolean value indicating the telephony state. 33 """ 34 pass 35 36 37class FlossTelephonyClient: 38 """Handles method calls and callbacks from the telephony interface.""" 39 40 TELEPHONY_SERVICE = 'org.chromium.bluetooth' 41 TELEPHONY_INTERFACE = 'org.chromium.bluetooth.BluetoothTelephony' 42 TELEPHONY_OBJECT_PATTERN = '/org/chromium/bluetooth/hci{}/telephony' 43 TELEPHONY_CB_INTF = 'org.chromium.bluetooth.BluetoothTelephonyCallback' 44 TELEPHONY_CB_OBJ_NAME = 'test_telephony_client' 45 46 class ExportedTelephonyCallbacks(observer_base.ObserverBase): 47 """ 48 <node> 49 <interface name="org.chromium.bluetooth.BluetoothTelephonyCallback"> 50 <method name="OnTelephonyUse"> 51 <arg type="s" name="add" direction="in" /> 52 <arg type="b" name="state" direction="in" /> 53 </method> 54 </interface> 55 </node> 56 """ 57 58 def __init__(self): 59 """Constructs exported callbacks object.""" 60 observer_base.ObserverBase.__init__(self) 61 62 def OnTelephonyUse(self, addr, state): 63 """Handles telephony use callback. 64 65 Args: 66 addr: The address of the telephony device. 67 state: The boolean value indicating the telephony state. 68 """ 69 70 for observer in self.observers.values(): 71 observer.on_telephony_use(addr, state) 72 73 def __init__(self, bus, hci): 74 """Constructs the client. 75 76 Args: 77 bus: D-Bus bus over which we'll establish connections. 78 hci: HCI adapter index. Get this value from `get_default_adapter` on FlossManagerClient. 79 """ 80 self.bus = bus 81 self.hci = hci 82 self.objpath = self.TELEPHONY_OBJECT_PATTERN.format(hci) 83 84 # We don't register callbacks by default. 85 self.callbacks = None 86 87 def __del__(self): 88 """Destructor.""" 89 del self.callbacks 90 91 @utils.glib_callback() 92 def on_telephony_use(self, addr, state): 93 """Handles telephony use callback. 94 95 Args: 96 addr: The address of the telephony device. 97 state: The boolean value indicating the telephony state. 98 """ 99 logging.debug('on_telephony_use: addr: %s, state: %s', addr, state) 100 101 def _make_dbus_phone_number(self, number): 102 """Makes struct for phone number D-Bus. 103 104 Args: 105 number : The phone number to use. 106 107 Returns: 108 Dictionary of phone number. 109 """ 110 return utils.dbus_optional_value('s', number) 111 112 @utils.glib_call(False) 113 def has_proxy(self): 114 """Checks whether telephony proxy can be acquired.""" 115 return bool(self.proxy()) 116 117 def proxy(self): 118 """Gets proxy object to telephony interface for method calls.""" 119 return self.bus.get(self.TELEPHONY_SERVICE, self.objpath)[self.TELEPHONY_INTERFACE] 120 121 @utils.glib_call(None) 122 def register_telephony_callback(self): 123 """Registers telephony callback for this client if one doesn't already exist. 124 125 Returns: 126 True on success, False on failure, None on DBus error. 127 """ 128 if self.callbacks: 129 return True 130 131 # Create and publish callbacks 132 self.callbacks = self.ExportedTelephonyCallbacks() 133 self.callbacks.add_observer('telephony_client', self) 134 objpath = utils.generate_dbus_cb_objpath(self.TELEPHONY_CB_OBJ_NAME, self.hci) 135 self.bus.register_object(objpath, self.callbacks, None) 136 137 # Register published callbacks with manager daemon 138 return self.proxy().RegisterTelephonyCallback(objpath) 139 140 @utils.glib_call(False) 141 def set_network_available(self, network_available): 142 """Sets network availability status. 143 144 Args: 145 network_available: A boolean value indicating whether the device is connected to the cellular network. 146 147 Returns: 148 True on success, False otherwise. 149 """ 150 self.proxy().SetNetworkAvailable(network_available) 151 return True 152 153 @utils.glib_call(False) 154 def set_roaming(self, roaming): 155 """Sets roaming mode. 156 157 Args: 158 roaming: A boolean value indicating whether the device is in roaming mode. 159 160 Returns: 161 True on success, False otherwise. 162 """ 163 self.proxy().SetRoaming(roaming) 164 return True 165 166 @utils.glib_call(None) 167 def set_signal_strength(self, signal_strength): 168 """Sets signal strength. 169 170 Args: 171 signal_strength: The signal strength value to be set, ranging from 0 to 5. 172 173 Returns: 174 True on success, False on failure, None on DBus error. 175 """ 176 return self.proxy().SetSignalStrength(signal_strength) 177 178 @utils.glib_call(None) 179 def set_battery_level(self, battery_level): 180 """Sets battery level. 181 182 Args: 183 battery_level: The battery level value to be set, ranging from 0 to 5. 184 185 Returns: 186 True on success, False on failure, None on DBus error. 187 """ 188 return self.proxy().SetBatteryLevel(battery_level) 189 190 @utils.glib_call(False) 191 def set_phone_ops_enabled(self, enable): 192 """Sets phone operations status. 193 194 Args: 195 enable: A boolean value indicating whether phone operations are enabled. 196 197 Returns: 198 True on success, False otherwise. 199 """ 200 self.proxy().SetPhoneOpsEnabled(enable) 201 return True 202 203 @utils.glib_call(False) 204 def set_mps_qualification_enabled(self, enable): 205 """Sets MPS qualification status. 206 207 Args: 208 enable: A boolean value indicating whether MPS qualification is enabled. 209 210 Returns: 211 True on success, False otherwise. 212 """ 213 self.proxy().SetMpsQualificationEnabled(enable) 214 return True 215 216 @utils.glib_call(None) 217 def incoming_call(self, number): 218 """Initiates an incoming call with the specified phone number. 219 220 Args: 221 number: The phone number of the incoming call. 222 223 Returns: 224 True on success, False on failure, None on DBus error. 225 """ 226 227 return self.proxy().IncomingCall(number) 228 229 @utils.glib_call(None) 230 def dialing_call(self, number): 231 """Initiates a dialing call with the specified phone number. 232 233 Args: 234 number: The phone number to dial. 235 236 Returns: 237 True on success, False on failure, None on DBus error. 238 """ 239 return self.proxy().DialingCall(number) 240 241 @utils.glib_call(None) 242 def answer_call(self): 243 """Answers an incoming or dialing call. 244 245 Returns: 246 True on success, False on failure, None on DBus error. 247 """ 248 return self.proxy().AnswerCall() 249 250 @utils.glib_call(None) 251 def hangup_call(self): 252 """Hangs up an active, incoming, or dialing call. 253 254 Returns: 255 True on success, False on failure, None on DBus error. 256 """ 257 return self.proxy().HangupCall() 258 259 @utils.glib_call(None) 260 def set_last_call(self, number=None): 261 """Sets last call with the specified phone number. 262 263 Args: 264 number: Optional phone number value to be set as the last call, Defaults to None if not provided. 265 Returns: 266 True on success, False on failure, None on DBus error. 267 """ 268 number = self._make_dbus_phone_number(number) 269 return self.proxy().SetLastCall(number) 270 271 @utils.glib_call(None) 272 def set_memory_call(self, number=None): 273 """Sets memory call with the specified phone number. 274 275 Args: 276 number: Optional phone number value to be set as the last call, Defaults to None if not provided. 277 278 Returns: 279 True on success, False on failure, None on DBus error. 280 """ 281 number = self._make_dbus_phone_number(number) 282 return self.proxy().SetMemoryCall(number) 283 284 @utils.glib_call(None) 285 def release_held(self): 286 """Releases all of the held calls. 287 288 Returns: 289 True on success, False on failure, None on DBus error. 290 """ 291 return self.proxy().ReleaseHeld() 292 293 @utils.glib_call(None) 294 def release_active_accept_held(self): 295 """Releases the active call and accepts a held call. 296 297 Returns: 298 True on success, False on failure, None on DBus error. 299 """ 300 return self.proxy().ReleaseActiveAcceptHeld() 301 302 @utils.glib_call(None) 303 def hold_active_accept_held(self): 304 """Holds the active call and accepts a held call. 305 306 Returns: 307 True on success, False on failure, None on DBus error. 308 """ 309 return self.proxy().HoldActiveAcceptHeld() 310 311 @utils.glib_call(None) 312 def audio_connect(self, address): 313 """Initiates an audio connection to the remote device. 314 315 Args: 316 address: The address of the remote device for audio connection. 317 318 Returns: 319 True on success, False on failure, None on DBus error. 320 """ 321 return self.proxy().AudioConnect(address) 322 323 @utils.glib_call(False) 324 def audio_disconnect(self, address): 325 """Disconnects the audio connection to the remote device. 326 327 Args: 328 address: The address of the remote device for audio disconnection. 329 330 Returns: 331 True on success, False otherwise. 332 """ 333 self.proxy().AudioDisconnect(address) 334 return True 335