usb_moded 0.86.0+mer64
usb_moded-control.c
Go to the documentation of this file.
1
24
25#include "usb_moded-control.h"
26
27#include "usb_moded.h"
30#include "usb_moded-log.h"
31#include "usb_moded-modes.h"
32#include "usb_moded-worker.h"
33
34/* Sanity check, configure should take care of this */
35#if defined SAILFISH_ACCESS_CONTROL && !defined SYSTEMD
36# error if SAILFISH_ACCESS_CONTROL is defined, SYSTEMD must be defined as well
37#endif
38
39/* ========================================================================= *
40 * Constants
41 * ========================================================================= */
42
53#define CONTROL_PENDING_USER_CHANGE_TIMEOUT (3000)
54
55/* ========================================================================= *
56 * Prototypes
57 * ========================================================================= */
58
59/* ------------------------------------------------------------------------- *
60 * CONTROL
61 * ------------------------------------------------------------------------- */
62
63uid_t control_get_user_for_mode (void);
64void control_set_user_for_mode (uid_t uid);
65const char *control_get_external_mode (void);
66static void control_set_external_mode (const char *mode);
67void control_clear_external_mode (void);
68static void control_update_external_mode (void);
69const char *control_get_target_mode (void);
70static void control_set_target_mode (const char *mode);
71void control_clear_target_mode (void);
72const char *control_get_selected_mode (void);
73void control_set_selected_mode (const char *mode);
74bool control_select_mode (const char *mode);
75const char *control_get_usb_mode (void);
76void control_clear_internal_mode (void);
77static void control_set_usb_mode (const char *mode);
78void control_mode_switched (const char *mode);
79static gboolean control_pending_user_change_cb (gpointer aptr);
80static bool control_have_pending_user_change (void);
81static void control_begin_pending_user_change(void);
82static void control_end_pending_user_change (void);
83void control_user_changed (void);
86void control_settings_changed (void);
88static bool control_get_enabled (void);
89void control_set_enabled (bool enable);
90static bool control_get_in_rescue_mode (void);
91static void control_set_in_rescue_mode (bool in_rescue_mode);
92static void control_rethink_usb_mode (void);
93void control_set_cable_state (cable_state_t cable_state);
94cable_state_t control_get_cable_state (void);
95void control_clear_cable_state (void);
97
98/* ========================================================================= *
99 * Data
100 * ========================================================================= */
101
102/* The external mode;
103 *
104 * What was the last current mode signaled over D-Bus.
105 */
106static char *control_external_mode = NULL;
107
108/* The target mode;
109 *
110 * What was the last target mode signaled over D-Bus.
111 */
112static char *control_target_mode = NULL;
113
118static char *control_internal_mode = NULL;
119
122static char *control_selected_mode = NULL;
123
130static cable_state_t control_cable_state = CABLE_STATE_UNKNOWN;
131
134static uid_t control_user_for_mode = UID_UNKNOWN;
135
138static guint control_pending_user_change_id = 0;
139
142static bool control_in_rescue_mode = false;
143
146static bool control_is_enabled = false;
147
148/* ========================================================================= *
149 * Functions
150 * ========================================================================= */
151
156uid_t
158{
159 return control_user_for_mode;
160}
161
166void
168{
169 LOG_REGISTER_CONTEXT;
170
171 if( control_user_for_mode != uid ) {
172 log_debug("control_user_for_mode: %d -> %d",
173 (int)control_user_for_mode, (int)uid);
174 control_user_for_mode = uid;
175 }
176}
177
178const char *control_get_external_mode(void)
179{
180 LOG_REGISTER_CONTEXT;
181
182 return control_external_mode ?: MODE_UNDEFINED;
183}
184
185static void control_set_external_mode(const char *mode)
186{
187 LOG_REGISTER_CONTEXT;
188
189 gchar *previous = control_external_mode;
190 if( !g_strcmp0(previous, mode) )
191 goto EXIT;
192
193 log_debug("external_mode: %s -> %s",
194 previous, mode);
195
196 control_external_mode = g_strdup(mode);
197 g_free(previous);
198
199 // DO THE DBUS BROADCAST
200
201 if( !strcmp(control_external_mode, MODE_ASK) ) {
202 /* send signal, mode will be set when the dialog service calls
203 * the set_mode method call. */
204 umdbus_send_event_signal(USB_CONNECTED_DIALOG_SHOW);
205 }
206
207 umdbus_send_current_state_signal(control_external_mode);
208
209 if( strcmp(control_external_mode, MODE_BUSY) ) {
210 /* Stable state reached. Synchronize target state.
211 *
212 * Note that normally this ends up being a nop,
213 * but might be needed if the originally scheduled
214 * target could not be reached due to errors / user
215 * disconnecting the cable.
216 */
217 control_set_target_mode(control_external_mode);
218 }
219
220EXIT:
221 return;
222}
223
224void control_clear_external_mode(void)
225{
226 LOG_REGISTER_CONTEXT;
227
228 g_free(control_external_mode),
229 control_external_mode = 0;
230}
231
232static void control_update_external_mode(void)
233{
234 LOG_REGISTER_CONTEXT;
235
236 const char *internal_mode = control_get_usb_mode();
237 const char *external_mode = common_map_mode_to_external(internal_mode);
238
239 control_set_external_mode(external_mode);
240}
241
242const char *control_get_target_mode(void)
243{
244 LOG_REGISTER_CONTEXT;
245
246 return control_target_mode ?: MODE_UNDEFINED;
247}
248
249static void control_set_target_mode(const char *mode)
250{
251 LOG_REGISTER_CONTEXT;
252
253 gchar *previous = control_target_mode;
254 if( !g_strcmp0(previous, mode) )
255 goto EXIT;
256
257 log_debug("target_mode: %s -> %s",
258 previous, mode);
259
260 control_target_mode = g_strdup(mode);
261 g_free(previous);
262
263 /* Cache settings that might be relevant for a dynamic mode, so
264 * that the same values are available and used for both entering
265 * and leaving the mode.
266 */
267 usbmoded_refresh_modedata(control_target_mode);
268
269 umdbus_send_target_state_signal(control_target_mode);
270
271EXIT:
272 return;
273}
274
275void control_clear_target_mode(void)
276{
277 LOG_REGISTER_CONTEXT;
278
279 g_free(control_target_mode),
280 control_target_mode = 0;
281}
282
288{
289 LOG_REGISTER_CONTEXT;
290 return control_selected_mode;
291}
292
297void control_set_selected_mode(const char *mode)
298{
299 LOG_REGISTER_CONTEXT;
300 char *prev = control_selected_mode;
301 if( g_strcmp0(prev, mode) ) {
302 log_debug("requested: %s -> %s", prev, mode);
303 control_selected_mode = mode ? g_strdup(mode) : 0;
304 g_free(prev);
305 }
306}
307
313bool control_select_mode(const char *mode)
314{
315 LOG_REGISTER_CONTEXT;
316
317 /* Update selected mode */
319
320 /* Re-evaluate active mode */
321 control_rethink_usb_mode();
322
323 /* Return true if active mode matches the requested one */
324 return !g_strcmp0(control_get_usb_mode(), mode);
325}
326
332const char * control_get_usb_mode(void)
333{
334 LOG_REGISTER_CONTEXT;
335
336 return control_internal_mode;
337}
338
339void control_clear_internal_mode(void)
340{
341 LOG_REGISTER_CONTEXT;
342
343 g_free(control_internal_mode),
344 control_internal_mode = 0;
345}
346
351static void control_set_usb_mode(const char *mode)
352{
353 LOG_REGISTER_CONTEXT;
354
355 /* Bookkeeping: Who activated this mode */
357
358 gchar *previous = control_internal_mode;
359 if( !g_strcmp0(previous, mode) )
360 goto EXIT;
361
362 log_debug("internal_mode: %s -> %s",
363 previous, mode);
364
365 control_internal_mode = g_strdup(mode);
366 g_free(previous);
367
368 /* Update target mode before declaring busy */
369 control_set_target_mode(control_internal_mode);
370
371 /* Invalidate current mode for the duration of mode transition */
372 control_set_external_mode(MODE_BUSY);
373
374 /* Propagate down to gadget config */
375 worker_request_hardware_mode(control_internal_mode);
376
377EXIT:
378 return;
379}
380
381/* Worker thread has finished mode switch
382 *
383 * @param mode The activated USB mode
384 */
385void control_mode_switched(const char *mode)
386{
387 LOG_REGISTER_CONTEXT;
388
389 /* Update state data - without retriggering the worker thread
390 */
391 if( g_strcmp0(control_internal_mode, mode) ) {
392 log_debug("internal_mode: %s -> %s",
393 control_internal_mode, mode);
394 g_free(control_internal_mode),
395 control_internal_mode = g_strdup(mode);
396 }
397
398 /* Propagate up to D-Bus */
399 control_update_external_mode();
400
401 return;
402}
403
406static gboolean control_pending_user_change_cb(gpointer aptr)
407{
408 (void)aptr;
409
410 if( control_pending_user_change_id ) {
411 log_debug("pending user change timeout");
412 control_pending_user_change_id = 0;
413 control_rethink_usb_mode();
414 }
415
416 return G_SOURCE_REMOVE;
417}
418
421static bool control_have_pending_user_change(void)
422{
423 return control_pending_user_change_id != 0;
424}
425
428static void control_begin_pending_user_change(void)
429{
430 if( !control_pending_user_change_id ) {
431 log_debug("pending user change started");
432 control_pending_user_change_id =
434 control_pending_user_change_cb, 0);
435 }
436}
437
440static void control_end_pending_user_change(void)
441{
442 if( control_pending_user_change_id ) {
443 log_debug("pending user change stopped");
444 g_source_remove(control_pending_user_change_id),
445 control_pending_user_change_id = 0;
446 }
447}
448
452{
453 log_debug("user = %d", (int)usbmoded_get_current_user());
454
455 /* We need to mask false positive "user is known and
456 * device is unlocked" blib arising from usb-moded
457 * getting user change notification before device lock
458 * status change -> start timer on user change and
459 * act as if device were locked until timer expires
460 * or device lock notification is received.
461 *
462 * But only for user changes that happen after the
463 * device bootup has been finished.
464 */
466 control_begin_pending_user_change();
467 else
468 control_end_pending_user_change();
469
470 /* Clear any mode selection done by the previous user
471 */
473
474 control_rethink_usb_mode();
475}
476
480{
481 log_debug("can_export = %d", usbmoded_can_export());
482
483 /* Device lock status change finalizes user change
484 */
485 control_end_pending_user_change();
486
487 control_rethink_usb_mode();
488}
489
493{
494 log_debug("in_usermode = %d; in_shutdown = %d",
496
497 control_rethink_usb_mode();
498}
499
503{
504 log_debug("settings changed");
505
506 control_rethink_usb_mode();
507}
508
512{
513 log_debug("init_done = %d", usbmoded_init_done_p());
514
515 control_rethink_usb_mode();
516}
517
520static bool control_get_enabled(void)
521{
522 return control_is_enabled;
523}
524
527void control_set_enabled(bool enable)
528{
529 if( control_is_enabled != enable ) {
530 control_is_enabled = enable;
531 log_debug("control_enabled = %d", control_is_enabled);
532
533 control_rethink_usb_mode();
534 }
535}
536
539static bool control_get_in_rescue_mode(void)
540{
541 return control_in_rescue_mode;
542}
543
546static void control_set_in_rescue_mode(bool in_rescue_mode)
547{
548 if( control_in_rescue_mode != in_rescue_mode ) {
549 log_debug("in_rescue_mode: %d -> %d",
550 control_in_rescue_mode, in_rescue_mode);
551 control_in_rescue_mode = in_rescue_mode;
552 }
553}
554
560static void control_rethink_usb_mode(void)
561{
562 LOG_REGISTER_CONTEXT;
563
564 uid_t current_user = usbmoded_get_current_user();
565 const char *current_mode = control_get_usb_mode();
566 cable_state_t cable_state = control_get_cable_state();
567 const char *mode_to_use = 0;
568 char *mode_to_free = 0;
569
570 /* Local setter function, to ease debugging */
571 auto const char *use_mode(const char *mode) {
572 if( g_strcmp0(mode_to_use, mode) ) {
573 log_debug("mode_to_use: %s -> %s",
574 mode_to_use ?: "unset",
575 mode ?: "unset");
576 mode_to_use = mode;
577 }
578 return mode_to_use;
579 }
580
581 log_debug("re-evaluating usb mode ...");
582
583 /* Local setter function, for dynamically allocated mode names */
584 auto const char *use_allocated_mode(char *mode) {
585 g_free(mode_to_free), mode_to_free = mode;
586 return use_mode(mode_to_free);
587 }
588
589 /* Defer mode selection until all noise resulting from
590 * usb-moded startup is over, we know that a suitable
591 * backend has been selected, etc.
592 */
593 if( !control_get_enabled() ) {
594 log_debug("starting up; mode changes blocked");
595 goto BAILOUT;
596 }
597
598 /* Handle cable disconnect / charger connect
599 *
600 * Only one mode is applicable regardless of things like current
601 * user, device lock status, etc.
602 */
603 if( cable_state != CABLE_STATE_PC_CONNECTED ) {
604 /* Reset bookkeeping that is relevant only for pc connection */
606 control_set_in_rescue_mode(false);
607
608 if( cable_state == CABLE_STATE_CHARGER_CONNECTED ) {
609 /* Charger connected
610 * -> CHARGER is the only options */
611 use_mode(MODE_CHARGER);
612 }
613 else {
614 /* Disconnected / unknown
615 * -> UNDEFINED is the only option */
616 use_mode(MODE_UNDEFINED);
617 }
618 goto MODESET;
619 }
620
621 /* Handle rescue mode override
622 *
623 * When booting up connected to a pc with rescue mode enabled,
624 * lock on to rescue mode until something else is explicitly
625 * requested / cable is detached.
626 */
627 if( usbmoded_get_rescue_mode() || control_get_in_rescue_mode() ) {
629 /* Rescue mode active
630 * -> DEVELOPER is the only option
631 *
632 */
633 use_mode(MODE_DEVELOPER);
634 control_set_in_rescue_mode(true);
635 goto MODESET;
636 }
637 }
638 control_set_in_rescue_mode(false);
639
640 /* Handle diagnostic mode override
641 */
642 if( usbmoded_get_diag_mode() ) {
643 /* Assumption is that in diag-mode there is only
644 * one mode configured i.e. list head is diag-mode. */
645 GList *iter = usbmoded_get_modelist();
646 if( !iter ) {
647 log_err("Diagnostic mode is not configured!");
648 use_mode(MODE_CHARGING_FALLBACK);
649 }
650 else {
651 log_debug("Entering diagnostic mode!");
652 modedata_t *data = iter->data;
653 use_mode(data->mode_name);
654 }
655 goto MODESET;
656 }
657
658 /* Handle bootup override
659 *
660 * Some modes (e.g. mtp) can require system to be in a
661 * state where external services can be started/stopped.
662 *
663 * Normalize situation by blocking all dynamic modes until
664 * bootup has been finished.
665 */
666 if( !usbmoded_init_done_p() ) {
667 log_debug("in bootup; dynamic modes blocked");
668 use_mode(MODE_CHARGING_FALLBACK);
669 goto MODESET;
670 }
671
672 /* Handle shutdown override
673 *
674 * In general initiating mode changes during shutdown
675 * makes little sense.
676 *
677 * Also, if developer mode is active, we want to keep it
678 * working as long as possible for debugging purposes.
679 *
680 * DSME reports shutdown intent before we are going to
681 * see user changes due to user session getting stopped.
682 * Once that happens
683 * -> ignore all changes and retain current mode
684 */
685 if( usbmoded_in_shutdown() ) {
686 log_debug("in shutdown, retaining '%s' mode", current_mode);
687 goto BAILOUT;
688 }
689
690 /* The rest of the mode selection logic must be subjected
691 * to filtering based on device lock status, current user, etc
692 */
693
694 /* By default use whatever user has selected
695 */
696 if( use_mode(control_get_selected_mode()) ) {
697 if( common_valid_mode(mode_to_use) ) {
698 /* Mode does not exist
699 * -> try setting */
700 log_debug("mode '%s' is not valid", mode_to_use);
701 use_mode(0);
702 }
703 else if( !usbmoded_is_mode_permitted(mode_to_use, current_user) ) {
704 /* Mode is not allowed
705 * -> try setting */
706 log_debug("mode '%s' is not permitted", mode_to_use);
707 use_mode(0);
708 }
709 }
710
711 /* If user has not selected anything, apply setting value */
712 if( !mode_to_use ) {
713 /* If current user is not determined, assume that device is
714 * booting up or in between two user sessions. Therefore we
715 * either must use whatever is configured as global default
716 * mode or let device lock to prevent the mode so that it can
717 * be set again once the device is unlocked */
718 uid_t uid = (current_user == UID_UNKNOWN) ? 0 : current_user;
719 use_allocated_mode(config_get_mode_setting(uid));
720 }
721
722 /* In case of ASK and only one mode from which to select,
723 * apply the only possibility available without prompting
724 * user.
725 */
726 if( !g_strcmp0(mode_to_use, MODE_ASK) ) {
727 if( current_user == UID_UNKNOWN ) {
728 /* ASK is valid only when there is user
729 * -> use fallback charging when user is not known */
730 log_debug("mode '%s' is not applicable", mode_to_use);
731 use_mode(MODE_CHARGING_FALLBACK);
732 } else {
733 // FIXME free() vs g_free() conflict
734 gchar *available = common_get_mode_list(AVAILABLE_MODES_LIST, current_user);
735 if( *available && !strchr(available, ',') ) {
736 use_allocated_mode(available), available = 0;
737 }
738 g_free(available);
739 }
740 }
741
742 /* After dealing with user selection and settings, check
743 * that we have mode that user is permitted to activate.
744 */
745 if( !mode_to_use ) {
746 /* Nothing selected -> silently choose fallback charging */
747 use_mode(MODE_CHARGING_FALLBACK);
748 }
749 else if( !strcmp(mode_to_use, MODE_CHARGING_FALLBACK) ) {
750 /* Fallback charging is not user selectable mode.
751 * As it is still expected to occur here, we need to skip
752 * the permission checks below to avoid logging noise.
753 */
754 }
755 else if( !usbmoded_is_mode_permitted(mode_to_use, current_user) ) {
756 log_debug("mode '%s' is not permitted", mode_to_use);
757 use_mode(MODE_CHARGING_FALLBACK);
758 }
759
760 /* Handle user change without mode change
761 *
762 * For example in case of mtp mode: we must terminate ongoing
763 * mtp session that exposes home directory of the previously
764 * active user -> activating fallback charging takes care of that.
765 *
766 * Assumption is that if we ever hit this condition, it will be
767 * followed by device lock state changes that will trigger exit
768 * from fallback charging.
769 */
770 if( control_get_user_for_mode() != current_user ) {
771 /* User did change */
772 if( !g_strcmp0(current_mode, mode_to_use) ) {
773 /* Mode to select did not change */
774 if( !common_modename_is_static(mode_to_use) ) {
775 /* Selected mode is dynamic
776 * -> redirect to fallback charging */
777 log_debug("mode '%s' must be terminated", mode_to_use);
778 use_mode(MODE_CHARGING_FALLBACK);
779 }
780 }
781 }
782
783 /* Blocking activation of dynamic modes
784 *
785 * Mode that is alreay active must be retained, but activating
786 * new dynamic modes while e.g. device is locked is not allowed.
787 */
788 if( control_have_pending_user_change() || !usbmoded_can_export() ) {
789 /* Device is locked / in ACT_DEAD / similar */
790 if( !g_strcmp0(mode_to_use, MODE_ASK) ) {
791 /* ASK is not valid while device is locked
792 * -> redirect to fallback charging */
793 log_debug("mode '%s' is not applicable", mode_to_use);
794 use_mode(MODE_CHARGING_FALLBACK);
795 }
796 else if( g_strcmp0(current_mode, mode_to_use) ) {
797 /* Mode to select did change */
798 if( !common_modename_is_static(mode_to_use) ) {
799 /* Selected mode is dynamic
800 * -> redirect to fallback charging */
801 log_debug("mode '%s' is not applicable", mode_to_use);
802 use_mode(MODE_CHARGING_FALLBACK);
803 }
804 }
805 }
806
807MODESET:
808 /* If no mode was selected, opt for fallback charging */
809 if( !mode_to_use )
810 use_mode(MODE_CHARGING_FALLBACK);
811
812 /* Activate the mode */
813 log_debug("selected mode = %s", mode_to_use);
814 control_set_usb_mode(mode_to_use);
815
816 /* Forget client request once it can't be honored */
817 if( g_strcmp0(control_get_selected_mode(), mode_to_use) )
819
820BAILOUT:
821 g_free(mode_to_free);
822}
823
828void control_set_cable_state(cable_state_t cable_state)
829{
830 LOG_REGISTER_CONTEXT;
831
832 cable_state_t prev = control_cable_state;
833 control_cable_state = cable_state;
834
835 if( control_cable_state == prev )
836 goto EXIT;
837
838 log_debug("control_cable_state: %s -> %s",
839 cable_state_repr(prev),
840 cable_state_repr(control_cable_state));
841
842 control_rethink_usb_mode();
843
844EXIT:
845 return;
846}
847
852cable_state_t control_get_cable_state(void)
853{
854 LOG_REGISTER_CONTEXT;
855
856 return control_cable_state;
857}
858
859void control_clear_cable_state(void)
860{
861 LOG_REGISTER_CONTEXT;
862
863 control_cable_state = CABLE_STATE_UNKNOWN;
864}
865
871{
872 LOG_REGISTER_CONTEXT;
873
874 bool connected = false;
875 switch( control_get_cable_state() ) {
876 case CABLE_STATE_CHARGER_CONNECTED:
877 case CABLE_STATE_PC_CONNECTED:
878 connected = true;
879 break;
880 default:
881 break;
882 }
883 return connected;
884}
int common_valid_mode(const char *mode)
gchar * common_get_mode_list(mode_list_type_t type, uid_t uid)
bool common_modename_is_static(const char *modename)
@ AVAILABLE_MODES_LIST
void control_settings_changed(void)
void control_set_cable_state(cable_state_t cable_state)
const char * control_get_usb_mode(void)
void control_device_state_changed(void)
void control_user_changed(void)
#define CONTROL_PENDING_USER_CHANGE_TIMEOUT
uid_t control_get_user_for_mode(void)
bool control_select_mode(const char *mode)
void control_init_done_changed(void)
void control_device_lock_changed(void)
void control_set_enabled(bool enable)
void control_set_selected_mode(const char *mode)
bool control_get_connection_state(void)
cable_state_t control_get_cable_state(void)
void control_set_user_for_mode(uid_t uid)
const char * control_get_selected_mode(void)
void umdbus_send_current_state_signal(const char *state_ind)
void umdbus_send_target_state_signal(const char *state_ind)
void umdbus_send_event_signal(const char *state_ind)
#define MODE_ASK
#define MODE_CHARGING_FALLBACK
#define MODE_UNDEFINED
#define MODE_CHARGER
#define MODE_BUSY
void usbmoded_refresh_modedata(const char *modename)
Definition usb_moded.c:295
bool usbmoded_in_usermode(void)
Definition usb_moded.c:569
GList * usbmoded_get_modelist(void)
Definition usb_moded.c:199
bool usbmoded_init_done_p(void)
Definition usb_moded.c:661
bool usbmoded_in_shutdown(void)
Definition usb_moded.c:585
bool usbmoded_can_export(void)
Definition usb_moded.c:627
uid_t usbmoded_get_current_user(void)
Definition usb_moded.c:608