usb_moded 0.86.0+mer58
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 umdbus_send_target_state_signal(control_target_mode);
264
265EXIT:
266 return;
267}
268
269void control_clear_target_mode(void)
270{
271 LOG_REGISTER_CONTEXT;
272
273 g_free(control_target_mode),
274 control_target_mode = 0;
275}
276
282{
283 LOG_REGISTER_CONTEXT;
284 return control_selected_mode;
285}
286
291void control_set_selected_mode(const char *mode)
292{
293 LOG_REGISTER_CONTEXT;
294 char *prev = control_selected_mode;
295 if( g_strcmp0(prev, mode) ) {
296 log_debug("requested: %s -> %s", prev, mode);
297 control_selected_mode = mode ? g_strdup(mode) : 0;
298 g_free(prev);
299 }
300}
301
307bool control_select_mode(const char *mode)
308{
309 LOG_REGISTER_CONTEXT;
310
311 /* Update selected mode */
313
314 /* Re-evaluate active mode */
315 control_rethink_usb_mode();
316
317 /* Return true if active mode matches the requested one */
318 return !g_strcmp0(control_get_usb_mode(), mode);
319}
320
326const char * control_get_usb_mode(void)
327{
328 LOG_REGISTER_CONTEXT;
329
330 return control_internal_mode;
331}
332
333void control_clear_internal_mode(void)
334{
335 LOG_REGISTER_CONTEXT;
336
337 g_free(control_internal_mode),
338 control_internal_mode = 0;
339}
340
345static void control_set_usb_mode(const char *mode)
346{
347 LOG_REGISTER_CONTEXT;
348
349 /* Bookkeeping: Who activated this mode */
351
352 gchar *previous = control_internal_mode;
353 if( !g_strcmp0(previous, mode) )
354 goto EXIT;
355
356 log_debug("internal_mode: %s -> %s",
357 previous, mode);
358
359 control_internal_mode = g_strdup(mode);
360 g_free(previous);
361
362 /* Update target mode before declaring busy */
363 control_set_target_mode(control_internal_mode);
364
365 /* Invalidate current mode for the duration of mode transition */
366 control_set_external_mode(MODE_BUSY);
367
368 /* Propagate down to gadget config */
369 worker_request_hardware_mode(control_internal_mode);
370
371EXIT:
372 return;
373}
374
375/* Worker thread has finished mode switch
376 *
377 * @param mode The activated USB mode
378 */
379void control_mode_switched(const char *mode)
380{
381 LOG_REGISTER_CONTEXT;
382
383 /* Update state data - without retriggering the worker thread
384 */
385 if( g_strcmp0(control_internal_mode, mode) ) {
386 log_debug("internal_mode: %s -> %s",
387 control_internal_mode, mode);
388 g_free(control_internal_mode),
389 control_internal_mode = g_strdup(mode);
390 }
391
392 /* Propagate up to D-Bus */
393 control_update_external_mode();
394
395 return;
396}
397
400static gboolean control_pending_user_change_cb(gpointer aptr)
401{
402 (void)aptr;
403
404 if( control_pending_user_change_id ) {
405 log_debug("pending user change timeout");
406 control_pending_user_change_id = 0;
407 control_rethink_usb_mode();
408 }
409
410 return G_SOURCE_REMOVE;
411}
412
415static bool control_have_pending_user_change(void)
416{
417 return control_pending_user_change_id != 0;
418}
419
422static void control_begin_pending_user_change(void)
423{
424 if( !control_pending_user_change_id ) {
425 log_debug("pending user change started");
426 control_pending_user_change_id =
428 control_pending_user_change_cb, 0);
429 }
430}
431
434static void control_end_pending_user_change(void)
435{
436 if( control_pending_user_change_id ) {
437 log_debug("pending user change stopped");
438 g_source_remove(control_pending_user_change_id),
439 control_pending_user_change_id = 0;
440 }
441}
442
446{
447 log_debug("user = %d", (int)usbmoded_get_current_user());
448
449 /* We need to mask false positive "user is known and
450 * device is unlocked" blib arising from usb-moded
451 * getting user change notification before device lock
452 * status change -> start timer on user change and
453 * act as if device were locked until timer expires
454 * or device lock notification is received.
455 *
456 * But only for user changes that happen after the
457 * device bootup has been finished.
458 */
460 control_begin_pending_user_change();
461 else
462 control_end_pending_user_change();
463
464 /* Clear any mode selection done by the previous user
465 */
467
468 control_rethink_usb_mode();
469}
470
474{
475 log_debug("can_export = %d", usbmoded_can_export());
476
477 /* Device lock status change finalizes user change
478 */
479 control_end_pending_user_change();
480
481 control_rethink_usb_mode();
482}
483
487{
488 log_debug("in_usermode = %d; in_shutdown = %d",
490
491 control_rethink_usb_mode();
492}
493
497{
498 log_debug("settings changed");
499
500 control_rethink_usb_mode();
501}
502
506{
507 log_debug("init_done = %d", usbmoded_init_done_p());
508
509 control_rethink_usb_mode();
510}
511
514static bool control_get_enabled(void)
515{
516 return control_is_enabled;
517}
518
521void control_set_enabled(bool enable)
522{
523 if( control_is_enabled != enable ) {
524 control_is_enabled = enable;
525 log_debug("control_enabled = %d", control_is_enabled);
526
527 control_rethink_usb_mode();
528 }
529}
530
533static bool control_get_in_rescue_mode(void)
534{
535 return control_in_rescue_mode;
536}
537
540static void control_set_in_rescue_mode(bool in_rescue_mode)
541{
542 if( control_in_rescue_mode != in_rescue_mode ) {
543 log_debug("in_rescue_mode: %d -> %d",
544 control_in_rescue_mode, in_rescue_mode);
545 control_in_rescue_mode = in_rescue_mode;
546 }
547}
548
554static void control_rethink_usb_mode(void)
555{
556 LOG_REGISTER_CONTEXT;
557
558 uid_t current_user = usbmoded_get_current_user();
559 const char *current_mode = control_get_usb_mode();
560 cable_state_t cable_state = control_get_cable_state();
561 const char *mode_to_use = 0;
562 char *mode_to_free = 0;
563
564 /* Local setter function, to ease debugging */
565 auto const char *use_mode(const char *mode) {
566 if( g_strcmp0(mode_to_use, mode) ) {
567 log_debug("mode_to_use: %s -> %s",
568 mode_to_use ?: "unset",
569 mode ?: "unset");
570 mode_to_use = mode;
571 }
572 return mode_to_use;
573 }
574
575 log_debug("re-evaluating usb mode ...");
576
577 /* Local setter function, for dynamically allocated mode names */
578 auto const char *use_allocated_mode(char *mode) {
579 g_free(mode_to_free), mode_to_free = mode;
580 return use_mode(mode_to_free);
581 }
582
583 /* Defer mode selection until all noise resulting from
584 * usb-moded startup is over, we know that a suitable
585 * backend has been selected, etc.
586 */
587 if( !control_get_enabled() ) {
588 log_debug("starting up; mode changes blocked");
589 goto BAILOUT;
590 }
591
592 /* Handle cable disconnect / charger connect
593 *
594 * Only one mode is applicable regardless of things like current
595 * user, device lock status, etc.
596 */
597 if( cable_state != CABLE_STATE_PC_CONNECTED ) {
598 /* Reset bookkeeping that is relevant only for pc connection */
600 control_set_in_rescue_mode(false);
601
602 if( cable_state == CABLE_STATE_CHARGER_CONNECTED ) {
603 /* Charger connected
604 * -> CHARGER is the only options */
605 use_mode(MODE_CHARGER);
606 }
607 else {
608 /* Disconnected / unknown
609 * -> UNDEFINED is the only option */
610 use_mode(MODE_UNDEFINED);
611 }
612 goto MODESET;
613 }
614
615 /* Handle rescue mode override
616 *
617 * When booting up connected to a pc with rescue mode enabled,
618 * lock on to rescue mode until something else is explicitly
619 * requested / cable is detached.
620 */
621 if( usbmoded_get_rescue_mode() || control_get_in_rescue_mode() ) {
623 /* Rescue mode active
624 * -> DEVELOPER is the only option
625 *
626 */
627 use_mode(MODE_DEVELOPER);
628 control_set_in_rescue_mode(true);
629 goto MODESET;
630 }
631 }
632 control_set_in_rescue_mode(false);
633
634 /* Handle diagnostic mode override
635 */
636 if( usbmoded_get_diag_mode() ) {
637 /* Assumption is that in diag-mode there is only
638 * one mode configured i.e. list head is diag-mode. */
639 GList *iter = usbmoded_get_modelist();
640 if( !iter ) {
641 log_err("Diagnostic mode is not configured!");
642 use_mode(MODE_CHARGING_FALLBACK);
643 }
644 else {
645 log_debug("Entering diagnostic mode!");
646 modedata_t *data = iter->data;
647 use_mode(data->mode_name);
648 }
649 goto MODESET;
650 }
651
652 /* Handle bootup override
653 *
654 * Some modes (e.g. mtp) can require system to be in a
655 * state where external services can be started/stopped.
656 *
657 * Normalize situation by blocking all dynamic modes until
658 * bootup has been finished.
659 */
660 if( !usbmoded_init_done_p() ) {
661 log_debug("in bootup; dynamic modes blocked");
662 use_mode(MODE_CHARGING_FALLBACK);
663 goto MODESET;
664 }
665
666 /* Handle shutdown override
667 *
668 * In general initiating mode changes during shutdown
669 * makes little sense.
670 *
671 * Also, if developer mode is active, we want to keep it
672 * working as long as possible for debugging purposes.
673 *
674 * DSME reports shutdown intent before we are going to
675 * see user changes due to user session getting stopped.
676 * Once that happens
677 * -> ignore all changes and retain current mode
678 */
679 if( usbmoded_in_shutdown() ) {
680 log_debug("in shutdown, retaining '%s' mode", current_mode);
681 goto BAILOUT;
682 }
683
684 /* The rest of the mode selection logic must be subjected
685 * to filtering based on device lock status, current user, etc
686 */
687
688 /* By default use whatever user has selected
689 */
690 if( use_mode(control_get_selected_mode()) ) {
691 if( common_valid_mode(mode_to_use) ) {
692 /* Mode does not exist
693 * -> try setting */
694 log_debug("mode '%s' is not valid", mode_to_use);
695 use_mode(0);
696 }
697 else if( !usbmoded_is_mode_permitted(mode_to_use, current_user) ) {
698 /* Mode is not allowed
699 * -> try setting */
700 log_debug("mode '%s' is not permitted", mode_to_use);
701 use_mode(0);
702 }
703 }
704
705 /* If user has not selected anything, apply setting value */
706 if( !mode_to_use ) {
707 /* If current user is not determined, assume that device is
708 * booting up or in between two user sessions. Therefore we
709 * either must use whatever is configured as global default
710 * mode or let device lock to prevent the mode so that it can
711 * be set again once the device is unlocked */
712 uid_t uid = (current_user == UID_UNKNOWN) ? 0 : current_user;
713 use_allocated_mode(config_get_mode_setting(uid));
714 }
715
716 /* In case of ASK and only one mode from which to select,
717 * apply the only possibility available without prompting
718 * user.
719 */
720 if( !g_strcmp0(mode_to_use, MODE_ASK) ) {
721 if( current_user == UID_UNKNOWN ) {
722 /* ASK is valid only when there is user
723 * -> use fallback charging when user is not known */
724 log_debug("mode '%s' is not applicable", mode_to_use);
725 use_mode(MODE_CHARGING_FALLBACK);
726 } else {
727 // FIXME free() vs g_free() conflict
728 gchar *available = common_get_mode_list(AVAILABLE_MODES_LIST, current_user);
729 if( *available && !strchr(available, ',') ) {
730 use_allocated_mode(available), available = 0;
731 }
732 g_free(available);
733 }
734 }
735
736 /* After dealing with user selection and settings, check
737 * that we have mode that user is permitted to activate.
738 */
739 if( !mode_to_use ) {
740 /* Nothing selected -> silently choose fallback charging */
741 use_mode(MODE_CHARGING_FALLBACK);
742 }
743 else if( !strcmp(mode_to_use, MODE_CHARGING_FALLBACK) ) {
744 /* Fallback charging is not user selectable mode.
745 * As it is still expected to occur here, we need to skip
746 * the permission checks below to avoid logging noise.
747 */
748 }
749 else if( !usbmoded_is_mode_permitted(mode_to_use, current_user) ) {
750 log_debug("mode '%s' is not permitted", mode_to_use);
751 use_mode(MODE_CHARGING_FALLBACK);
752 }
753
754 /* Handle user change without mode change
755 *
756 * For example in case of mtp mode: we must terminate ongoing
757 * mtp session that exposes home directory of the previously
758 * active user -> activating fallback charging takes care of that.
759 *
760 * Assumption is that if we ever hit this condition, it will be
761 * followed by device lock state changes that will trigger exit
762 * from fallback charging.
763 */
764 if( control_get_user_for_mode() != current_user ) {
765 /* User did change */
766 if( !g_strcmp0(current_mode, mode_to_use) ) {
767 /* Mode to select did not change */
768 if( !common_modename_is_static(mode_to_use) ) {
769 /* Selected mode is dynamic
770 * -> redirect to fallback charging */
771 log_debug("mode '%s' must be terminated", mode_to_use);
772 use_mode(MODE_CHARGING_FALLBACK);
773 }
774 }
775 }
776
777 /* Blocking activation of dynamic modes
778 *
779 * Mode that is alreay active must be retained, but activating
780 * new dynamic modes while e.g. device is locked is not allowed.
781 */
782 if( control_have_pending_user_change() || !usbmoded_can_export() ) {
783 /* Device is locked / in ACT_DEAD / similar */
784 if( !g_strcmp0(mode_to_use, MODE_ASK) ) {
785 /* ASK is not valid while device is locked
786 * -> redirect to fallback charging */
787 log_debug("mode '%s' is not applicable", mode_to_use);
788 use_mode(MODE_CHARGING_FALLBACK);
789 }
790 else if( g_strcmp0(current_mode, mode_to_use) ) {
791 /* Mode to select did change */
792 if( !common_modename_is_static(mode_to_use) ) {
793 /* Selected mode is dynamic
794 * -> redirect to fallback charging */
795 log_debug("mode '%s' is not applicable", mode_to_use);
796 use_mode(MODE_CHARGING_FALLBACK);
797 }
798 }
799 }
800
801MODESET:
802 /* If no mode was selected, opt for fallback charging */
803 if( !mode_to_use )
804 use_mode(MODE_CHARGING_FALLBACK);
805
806 /* Activate the mode */
807 log_debug("selected mode = %s", mode_to_use);
808 control_set_usb_mode(mode_to_use);
809
810 /* Forget client request once it can't be honored */
811 if( g_strcmp0(control_get_selected_mode(), mode_to_use) )
813
814BAILOUT:
815 g_free(mode_to_free);
816}
817
822void control_set_cable_state(cable_state_t cable_state)
823{
824 LOG_REGISTER_CONTEXT;
825
826 cable_state_t prev = control_cable_state;
827 control_cable_state = cable_state;
828
829 if( control_cable_state == prev )
830 goto EXIT;
831
832 log_debug("control_cable_state: %s -> %s",
833 cable_state_repr(prev),
834 cable_state_repr(control_cable_state));
835
836 control_rethink_usb_mode();
837
838EXIT:
839 return;
840}
841
846cable_state_t control_get_cable_state(void)
847{
848 LOG_REGISTER_CONTEXT;
849
850 return control_cable_state;
851}
852
853void control_clear_cable_state(void)
854{
855 LOG_REGISTER_CONTEXT;
856
857 control_cable_state = CABLE_STATE_UNKNOWN;
858}
859
865{
866 LOG_REGISTER_CONTEXT;
867
868 bool connected = false;
869 switch( control_get_cable_state() ) {
870 case CABLE_STATE_CHARGER_CONNECTED:
871 case CABLE_STATE_PC_CONNECTED:
872 connected = true;
873 break;
874 default:
875 break;
876 }
877 return connected;
878}
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
bool usbmoded_in_usermode(void)
Definition usb_moded.c:533
GList * usbmoded_get_modelist(void)
Definition usb_moded.c:197
bool usbmoded_init_done_p(void)
Definition usb_moded.c:625
bool usbmoded_in_shutdown(void)
Definition usb_moded.c:549
bool usbmoded_can_export(void)
Definition usb_moded.c:591
uid_t usbmoded_get_current_user(void)
Definition usb_moded.c:572