File Coverage

blib/lib/Net/Radio/oFono.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::Radio::oFono;
2              
3 1     1   22062 use 5.010;
  1         4  
  1         46  
4 1     1   5 use strict;
  1         2  
  1         111  
5 1     1   6 use warnings;
  1         6  
  1         45  
6              
7             =head1 NAME
8              
9             Net::Radio::oFono - Perl API to oFono
10              
11             =cut
12              
13             our $VERSION = '0.001';
14              
15 1     1   569 use Net::Radio::oFono::Manager;
  0            
  0            
16             use Net::Radio::oFono::Modem;
17             use Net::Radio::oFono::SimManager;
18             use Net::Radio::oFono::RadioSettings;
19             use Net::Radio::oFono::NetworkRegistration;
20             use Net::Radio::oFono::ConnectionManager;
21             use Net::Radio::oFono::MessageManager;
22             use Net::Radio::oFono::MessageWaiting;
23             use Net::Radio::oFono::CellBroadcast;
24              
25             use Log::Any qw($log);
26              
27             use base qw(Net::Radio::oFono::Helpers::EventMgr);
28              
29             =head1 SYNOPSIS
30              
31             This is the frontend API to communicate with the oFono daemon over DBus.
32              
33             use Net::Radio::oFono;
34              
35             my $oFono = Net::Radio::oFono->new();
36             my @modems = $oFono->get_modems();
37             foreach my $modem_path (@modems)
38             {
39             my $nwreg = $oFono->get_modem_interface("NetworkRegistration");
40             if( $nwreg )
41             {
42             if( $nwreg->GetProperty("Status", 1) eq "registered" )
43             {
44             say "Network for modem '" . $modem_path . "': ", $nwreg->GetProperty("Name");
45             }
46             else
47             {
48             say "Network for modem '" . $modem_path . "' is in status ", $nwreg->GetProperty("Status");
49             }
50             }
51             else
52             {
53             say "No network registration for modem $modem_path";
54             }
55             }
56            
57             # or use the event API
58             my $oFono = Net::Radio::oFono->new(
59             "ON_NETWORKREGISTRATION_PROPERTY_NAME_CHANGED" => sub {
60             my ( $ofono, $event, $info ) = @_;
61             my ( $modem_path, $name ) = @$info;
62             say "Network for modem '" . $modem_path . "': ", $name;
63             },
64             ...
65             );
66              
67             =head1 INHERITANCE
68              
69             Net::Radio::oFono
70             ISA Net::Radio::oFono::Helpers::EventMgr
71              
72             =head1 EVENTS
73              
74             Instances of Net::Radio::oFono can trigger following events (all
75             characters are upper cased):
76              
77             =over 16
78              
79             =item ON_MODEM_ADDED
80              
81             Triggered when a new modem is added. On initialization, it is triggered
82             for each found modem.
83              
84             Parameters:
85              
86             =over 20
87              
88             =item $modem_path
89              
90             D-Bus object path to the modem oject.
91              
92             =back
93              
94             =item ON_MODEM_REMOVED
95              
96             Triggered when a modem is removed. On destrcution, it is triggered
97             for each remaining modem.
98              
99             Parameters:
100              
101             =over 20
102              
103             =item $modem_path
104              
105             D-Bus object path to the modem oject.
106              
107             =back
108              
109             =item ON_MODEM_INTERFACE_ADDED
110              
111             Triggered when an interface is added to a modem object.
112             Modem interfaces depend on the modem states, for example the I
113             interface is always available when the modem is I.
114              
115             Parameters:
116              
117             =over 20
118              
119             =item $modem_path
120              
121             D-Bus object path to the modem oject.
122              
123             =item $interface
124              
125             The name of the propagated interface (eg. I, I, ...)
126              
127             =back
128              
129             =item ON_MODEM_INTERFACE_ . $interface . _ADDED
130              
131             Triggered when an interface is added to a modem object.
132             Modem interfaces depend on the modem states, for example the I
133             interface is always available when the modem is I.
134              
135             The advantage of this event compared to I is the
136             delegation of the dispatching for different interfaces to the EventMgr.
137              
138             Parameters:
139              
140             =over 20
141              
142             =item $modem_path
143              
144             D-Bus object path to the modem oject.
145              
146             =back
147              
148             =item ON_MODEM_INTERFACE_REMOVED
149              
150             Triggered when an interface is removed from a modem object.
151             Modem interfaces depend on the modem states, for example the I
152             interface is always available when the modem is I and will be
153             removed when the modem is turned off.
154              
155             Parameters:
156              
157             =over 20
158              
159             =item $modem_path
160              
161             D-Bus object path to the modem oject.
162              
163             =item $interface
164              
165             The name of the propagated interface (eg. I, I, ...)
166              
167             =back
168              
169             =item ON_MODEM_INTERFACE_ . $interface . _REMOVED
170              
171             Triggered when an interface is removed from a modem object.
172             Modem interfaces depend on the modem states, for example the I
173             interface is always available when the modem is I and will be
174             removed when the modem is turned off.
175              
176             The advantage of this event compared to I is the
177             delegation of the dispatching for different interfaces to the EventMgr.
178              
179             Parameters:
180              
181             =over 20
182              
183             =item $modem_path
184              
185             D-Bus object path to the modem oject.
186              
187             =back
188              
189             =item ON_ . $interface . _PROPERTY_CHANGED
190              
191             Triggered when a property of a modem object or modem interface object
192             is changed. I is handled as any other interface for this event.
193              
194             Typical name for such an event is I.
195              
196             Parameters:
197              
198             =over 20
199              
200             =item $modem_path
201              
202             D-Bus object path to the modem oject.
203              
204             =item $property
205              
206             Name of the changed property.
207              
208             =back
209              
210             =item ON_ . $interface . _PROPERTY_ . $property . _CHANGED
211              
212             Triggered when a property of a modem object or modem interface object
213             is changed. I is handled as any other interface for this event.
214              
215             Typical name for such an event is I.
216              
217             The advantage of this event compared to I
218             is the delegation of the dispatching for different properties to the EventMgr.
219              
220             Parameters:
221              
222             =over 20
223              
224             =item $modem_path
225              
226             D-Bus object path to the modem oject.
227              
228             =item $property_value
229              
230             The value of the changed property.
231              
232             =back
233              
234             =back
235              
236             =head1 SUBROUTINES/METHODS
237              
238             =head2 new(%events)
239              
240             Instantiates new oFono frontend accessor, registers specified events and
241             initializes the modem manager. Events between frontend accessor and
242             wrapper classes are separated.
243              
244             =cut
245              
246             sub new
247             {
248             my ( $class, %events ) = @_;
249              
250             my $self = __PACKAGE__->SUPER::new(%events);
251             bless( $self, __PACKAGE__ );
252             $self->_init();
253              
254             return $self;
255             }
256              
257             =head2 _init()
258              
259             Initializes the frontend accessor component:
260              
261             =over 4
262              
263             =item *
264              
265             Instantiates L.
266              
267             =item *
268              
269             Instantiates L for each already known modem device
270             using L.
271              
272             =item *
273              
274             Registers events C and C on the manager.
275              
276             =back
277              
278             =cut
279              
280             sub _init
281             {
282             my $self = shift;
283              
284             $self->{manager} = Net::Radio::oFono::Manager->new();
285              
286             my %modems = $self->{manager}->GetModems();
287             $self->{modems} = {};
288             foreach my $modem_path ( keys %modems )
289             {
290             $self->_add_modem($modem_path);
291             }
292              
293             $self->{manager}->add_event( "ON_MODEM_ADDED", \&on_modem_added, $self );
294             $self->{manager}->add_event( "ON_MODEM_REMOVED", \&on_modem_removed, $self );
295              
296             return $self;
297             }
298              
299             sub DESTROY
300             {
301             my $self = $_[0];
302              
303             foreach my $modem_path ( keys %{ $self->{modems} } )
304             {
305             $self->_remove_modem($modem_path);
306             }
307              
308             delete $self->{modems};
309             delete $self->{manager};
310              
311             return;
312             }
313              
314             =head2 _add_modem
315              
316             Internal method to properly add a modem to the frontend for accessing it.
317              
318             Registers the events C and
319             C on the created object.
320              
321             Triggers the event C when finished with that procedure.
322              
323             =cut
324              
325             sub _add_modem
326             {
327             my ( $self, $modem_path ) = @_;
328              
329             my $modem = Net::Radio::oFono::Modem->new($modem_path);
330             $self->{modems}->{$modem_path}->{Modem} = $modem;
331              
332             $modem->add_event( "ON_PROPERTY_CHANGED", \&on_property_changed, $self );
333             $modem->add_event( "ON_PROPERTY_INTERFACES_CHANGED", \&on_modem_interfaces_changed, $self );
334              
335             $self->_update_modem_interfaces($modem);
336              
337             $self->trigger_event( "ON_MODEM_ADDED", $modem_path );
338              
339             return;
340             }
341              
342             =head2 _remove_modem
343              
344             Internal method to properly remove a modem from the frontend for accessing it.
345              
346             Removes all interfaces objects from the modem using
347             L mocking and empty interface list and finally
348             destroy the modem object itself.
349              
350             Triggers C when the procedure has completed.
351              
352             =cut
353              
354             sub _remove_modem
355             {
356             my ( $self, $modem_path ) = @_;
357              
358             defined( $self->{modems}->{$modem_path} ) or return;
359              
360             $self->_update_modem_interfaces( $self->{modems}->{$modem_path}->{Modem}, [] );
361             delete $self->{modems}->{$modem_path};
362              
363             $self->trigger_event( "ON_MODEM_REMOVED", $modem_path );
364              
365             return;
366             }
367              
368             sub _create_remote_obj
369             {
370             my ( $self, $if_class, $obj_path ) = @_;
371             my %events = ();
372              
373             $if_class->DOES("Net::Radio::oFono::Roles::Properties")
374             and $events{ON_PROPERTY_CHANGED} = {
375             FUNC => \&on_property_changed,
376             MEMO => $self
377             };
378             if ( $if_class->DOES("Net::Radio::oFono::Roles::Manager") )
379             {
380             my $type = uc( $if_class->_get_managed_type() );
381             $events{ "ON_" . $type . "_ADDED" } = {
382             FUNC => \&on_object_added,
383             MEMO => $self
384             };
385             $events{ "ON_" . $type . "_REMOVED" } = {
386             FUNC => \&on_object_removed,
387             MEMO => $self
388             };
389             }
390              
391             if ( $if_class->can("_extra_events") )
392             {
393             my @extra_events = $if_class->_extra_events();
394             foreach my $extra_event (@extra_events)
395             {
396             $events{$extra_event} = {
397             FUNC => \&on_extra_event,
398             MEMO => $self
399             };
400             }
401             }
402              
403             return $if_class->new( $obj_path, %events );
404             }
405              
406             =head2 _update_modem_interfaces
407              
408             Internal function to adjust the interface objects of a remote modem objects.
409             It iterates over the list of the available interfaces of the "Interfaces"
410             property of the modem object to instantiate a new interface object for newly
411             added ones and removes those objects of interfaces which are removed.
412              
413             Triggers the events C and
414             C for each newly instantiated
415             interface. Triggers the events C and
416             C for each interface which
417             was removed.
418              
419             =cut
420              
421             sub _update_modem_interfaces
422             {
423             my ( $self, $modem, $interfaces ) = @_;
424             $interfaces //= $modem->GetProperty("Interfaces");
425             my @interface_list = map { ( my $pure = $_ ) =~ s/org.ofono.//; $pure } @$interfaces;
426             my $if_instances = $self->{modems}->{ $modem->modem_path() };
427             my %superflous_if_instances = map { $_ => 1 } keys %$if_instances;
428             delete $superflous_if_instances{Modem};
429              
430             foreach my $interface (@interface_list)
431             {
432             delete $superflous_if_instances{$interface};
433             my $if_class = "Net::Radio::oFono::$interface";
434             $if_class->isa("Net::Radio::oFono::Modem") or next;
435             defined( $if_instances->{$interface} ) and next;
436             my $modem_path = $modem->modem_path();
437             $if_instances->{$interface} = $self->_create_remote_obj( $if_class, $modem_path );
438             $self->trigger_event( "ON_MODEM_INTERFACE_ADDED", [ $modem_path, $interface ] );
439             $self->trigger_event( "ON_MODEM_INTERFACE_" . uc($interface) . "_ADDED", $modem_path );
440             }
441              
442             foreach my $interface ( keys %superflous_if_instances )
443             {
444             delete $if_instances->{$interface};
445             my $modem_path = $modem->modem_path();
446             $self->trigger_event( "ON_MODEM_INTERFACE_REMOVED", [ $modem_path, $interface ] );
447             $self->trigger_event( "ON_MODEM_INTERFACE_" . uc($interface) . "_REMOVED", $modem_path );
448             }
449              
450             return;
451             }
452              
453             =head2 get_modems(;$force)
454              
455             Returns the list of object path's for currently known (and instantiated)
456             modem objects.
457              
458             TODO: implement way to get without Net::DBus::Reactor->main->run() ...
459              
460             =cut
461              
462             sub get_modems
463             {
464             my ( $self, $force ) = @_;
465              
466             $force and $self->{manager}->GetModems(1);
467              
468             return keys %{ $self->{modems} };
469             }
470              
471             =head2 get_modem_interface($modem_path,$interface)
472              
473             Returns the object for the specified interface on the given modem object.
474             If either the modem device isn't known or the interface isn't available
475             yet, it returns nothing.
476              
477             =cut
478              
479             sub get_modem_interface
480             {
481             my ( $self, $modem_path, $if_name ) = @_;
482             defined( $self->{modems}->{$modem_path} )
483             and defined( $self->{modems}->{$modem_path}->{$if_name} )
484             and return $self->{modems}->{$modem_path}->{$if_name};
485             return;
486             }
487              
488             sub _add_managed_object
489             {
490             my ( $self, $mgr_type, $mgmt_type, $obj_path ) = @_;
491              
492             my $hn = lc($mgmt_type) . "s";
493             my $if_class = "Net::Radio::oFono::" . $mgmt_type;
494              
495             $self->{$hn}->{$obj_path}->{$mgmt_type} = $self->_create_remote_obj( $if_class, $obj_path );
496              
497             $self->trigger_event( "ON_" . uc($mgr_type) . "_" . uc($mgmt_type) . "_ADDED", $obj_path );
498              
499             return;
500             }
501              
502             sub _remove_managed_object
503             {
504             my ( $self, $mgr_type, $mgmt_type, $obj_path ) = @_;
505              
506             my $hn = lc($mgmt_type) . "s";
507             defined( $self->{$hn}->{$obj_path} ) or return;
508              
509             delete $self->{$hn}->{$obj_path};
510              
511             $self->trigger_event( "ON_" . uc($mgr_type) . "_" . uc($mgmt_type) . "_REMOVED", $obj_path );
512              
513             return;
514             }
515              
516             =head2 get_remote_object($type,$obj_path;$if_name)
517              
518             Returns the object for the specified remote object on the given object path.
519             Missing $if_name is replaced by $type.
520              
521             If one of $type, $obj_path or $if_name isn't available, nothing is returned.
522              
523             =cut
524              
525             sub get_remote_object
526             {
527             my ( $self, $type, $obj_path, $if_name ) = @_;
528             my $hn = lc($type) . "s";
529             $if_name //= $type;
530              
531             defined( $self->{$hn} ) or return $self->get_modem_interface( $obj_path, $if_name );
532             defined( $self->{$hn}->{$obj_path} )
533             and defined( $self->{$hn}->{$obj_path}->{$if_name} )
534             and return $self->{$hn}->{$obj_path}->{$if_name};
535              
536             return;
537             }
538              
539             =head2 on_modem_added
540              
541             Invoked when the even C is triggered by the modem
542             manager and invokes L for the submitted object path.
543              
544             =cut
545              
546             sub on_modem_added
547             {
548             my ( $self, $manager, $event, $modem_path ) = @_;
549              
550             $self->_add_modem($modem_path);
551              
552             return;
553             }
554              
555             =head2 on_modem_removed
556              
557             Invoked when the even C is triggered by the modem
558             manager and invokes L for the submitted object path.
559              
560             =cut
561              
562             sub on_modem_removed
563             {
564             my ( $self, $manager, $event, $modem_path ) = @_;
565              
566             $self->_remove_modem($modem_path);
567              
568             return;
569             }
570              
571             =head2 on_modem_interfaces_changed
572              
573             Triggered when a modem object changes it's list of available interfaces
574             in addition to L with C as name
575             of the changed property.
576              
577             Updates active interface objects using L.
578              
579             =cut
580              
581             sub on_modem_interfaces_changed
582             {
583             my ( $self, $modem, $event_name, $interfaces ) = @_;
584              
585             $self->_update_modem_interfaces( $modem, $interfaces );
586              
587             return;
588             }
589              
590             =head2 on_object_added
591              
592             Invoked when the even C is triggered by any modem
593             interface with manager role when an embedded object is added. Triggers
594             event C<"ON_" . uc($mgr_type) . "_" . uc($mgmt_type) . "_ADDED"> with
595             the object path of the created object as parameter.
596              
597             When a new context is added in ConnectionManager, the event
598             C is triggered.
599              
600             =cut
601              
602             sub on_object_added
603             {
604             my ( $self, $obj_mgr, $event, $obj_path ) = @_;
605             my $mgmt_type = $obj_mgr->_get_managed_type();
606             ( my $mgr_type = ref($obj_mgr) ) =~ s/Net::Radio::oFono:://;
607              
608             $self->_add_managed_object( $mgr_type, $mgmt_type, $obj_path );
609              
610             return;
611             }
612              
613             =head2 on_object_removed
614              
615             Invoked when the even C is triggered by any modem
616             interface with manager role when an embedded object is removed. Triggers
617             event C<"ON_" . uc($mgr_type) . "_" . uc($mgmt_type) . "_ADDED"> with the
618             object path of the created object as parameter.
619              
620             When a context is removed from ConnectionManager, the event
621             C is triggered.
622              
623             =cut
624              
625             sub on_object_removed
626             {
627             my ( $self, $obj_mgr, $event, $obj_path ) = @_;
628             my $mgmt_type = $obj_mgr->_get_managed_type();
629             ( my $mgr_type = ref($obj_mgr) ) =~ s/Net::Radio::oFono:://;
630              
631             $self->_remove_managed_object( $mgr_type, $mgmt_type, $obj_path );
632              
633             return;
634             }
635              
636             =head2 on_property_changed
637              
638             Triggered when a modem object modifies a property.
639              
640             Triggers C with modem path and
641             property name as parameter as well as
642             C with
643             modem path and property value as parameter.
644              
645             =cut
646              
647             sub on_property_changed
648             {
649             my ( $self, $obj, $event_name, $property ) = @_;
650             my $modem_path = $obj->modem_path();
651             ( my $if_name = ref($obj) ) =~ s/.*://;
652              
653             $self->trigger_event( "ON_" . uc($if_name) . "_PROPERTY_CHANGED", [ $modem_path, $property ] );
654             $self->trigger_event( "ON_" . uc($if_name) . "_PROPERTY_" . uc($property) . "_CHANGED",
655             [ $modem_path, $obj->GetProperty($property) ] );
656              
657             return;
658             }
659              
660             =head2 on_extra_event
661              
662             Triggered when any managed remote object fires an event which is not
663             usually watched. This event is enriched with some information about the
664             original sender:
665              
666             =over 4
667              
668             =item *
669              
670             The name of the event is modified by placing the upper cased basename of
671             the triggering object interface directly behind the leading "ON_".
672              
673             =item *
674              
675             The list of arguments is prepended by the basename of the triggering
676             object and the object path of the triggering object.
677              
678             =back
679              
680             Use L
681             to get the object instance.
682              
683             =cut
684              
685             sub on_extra_event
686             {
687             my ( $self, $obj, $event_name, $arg ) = @_;
688              
689             my @event_parts = split( "_", $event_name );
690             ( my $type = ref($obj) ) =~ s/Net::Radio::oFono:://;
691             splice( @event_parts, 1, 0, uc($type) );
692              
693             my $fwd_arg =
694             _ARRAY($arg) ? [ $type, $obj->obj_path(), @$arg ] : [ $type, $obj->obj_path(), $arg ];
695             $self->trigger_event( join( "_", @event_parts ), $fwd_arg );
696              
697             return;
698             }
699              
700             =head1 BUGS
701              
702             Please report any bugs or feature requests to C, or through
703             the web interface at L. I will be notified, and then you'll
704             automatically be notified of progress on your bug as I make changes.
705              
706             If you think you've found a bug then please read "How to Report Bugs
707             Effectively" by Simon Tatham:
708             L.
709              
710             =head1 SUPPORT
711              
712             You can find documentation for this module with the perldoc command.
713              
714             perldoc Net::Radio::oFono
715              
716             You can also look for information at:
717              
718             =over 4
719              
720             =item * RT: CPAN's request tracker (report bugs here)
721              
722             L
723              
724             If you think you've found a bug then please read "How to Report Bugs
725             Effectively" by Simon Tatham:
726             L.
727              
728             =item * AnnoCPAN: Annotated CPAN documentation
729              
730             L
731              
732             =item * CPAN Ratings
733              
734             L
735              
736             =item * Search CPAN
737              
738             L
739              
740             =back
741              
742             =head2 Where can I go for help with a concrete version?
743              
744             Bugs and feature requests are accepted against the latest version
745             only. To get patches for earlier versions, you need to get an
746             agreement with a developer of your choice - who may or not report the
747             issue and a suggested fix upstream (depends on the license you have
748             chosen).
749              
750             =head2 Business support and maintenance
751              
752             For business support you can contact Jens via his CPAN email
753             address rehsackATcpan.org. Please keep in mind that business
754             support is neither available for free nor are you eligible to
755             receive any support based on the license distributed with this
756             package.
757              
758             =head1 ACKNOWLEDGEMENTS
759              
760             At first the guys from the oFono-Team shall be named: Marcel Holtmann and
761             Denis Kenzior, the maintainers and all the people named in ofono/AUTHORS.
762             Without their effort, there would no need for a Net::Radio::oFono module.
763              
764             Further, Peter "ribasushi" Rabbitson helped a lot by providing hints
765             and support how to make this API accessor a valuable CPAN module.
766              
767             =head1 AUTHOR
768              
769             Jens Rehsack, C<< >>
770              
771             =head1 LICENSE AND COPYRIGHT
772              
773             Copyright 2012 Jens Rehsack.
774              
775             This program is free software; you can redistribute it and/or modify it
776             under the terms of either: the GNU General Public License as published
777             by the Free Software Foundation; or the Artistic License.
778              
779             See http://dev.perl.org/licenses/ for more information.
780              
781             =cut
782              
783             1; # End of Net::Radio::oFono