/* * Copyright 2013 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authors: * Charles Kerr */ /** * Monitors whether or not bluetooth is blocked, * either by software (e.g., a session configuration setting) * or by hardware (e.g., user disabled it via a physical switch on her laptop). * * KillSwitchBluetooth uses this as the impl for its Bluetooth.blocked property */ public interface KillSwitch: Object { public abstract bool is_valid (); public abstract bool blocked { get; protected set; } /* Try to block/unblock bluetooth. * This can fail if the requested state is overruled by a hardware block. */ public abstract void try_set_blocked (bool blocked); } /** * KillSwitch impementation for Linux using /dev/rfkill */ public class RfKillSwitch: KillSwitch, Object { public bool blocked { get; protected set; default = false; } public void try_set_blocked (bool blocked) { return_if_fail (this.blocked != blocked); // Try to soft-block all the bluetooth devices var event = Linux.RfKillEvent() { op = Linux.RfKillOp.CHANGE_ALL, type = Linux.RfKillType.BLUETOOTH, soft = (uint8)blocked }; /* Write this request to rfkill. * Don't update this object's "blocked" property here -- * We'll get the update when on_channel_event() reads it below */ var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent)); if (bwritten == -1) warning (@"Could not write rfkill event: $(strerror(errno))"); } /* represents an entry that we've read from the rfkill file */ private class Entry { public uint32 idx; public Linux.RfKillType type; public bool soft; public bool hard; } private HashTable entries; private int fd = -1; private IOChannel channel; private uint watch; protected override void dispose () { if (watch != 0) { Source.remove (watch); watch = 0; } if (fd != -1) { Posix.close (fd); fd = -1; } base.dispose (); } public RfKillSwitch () { entries = new HashTable(direct_hash, direct_equal); var path = "/dev/rfkill"; fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK ); if (fd == -1) { warning (@"Can't open $path for use as a killswitch backend: $(strerror(errno))"); } else { // read everything that's already there, then watch for more while (read_event()); channel = new IOChannel.unix_new (fd); watch = channel.add_watch (IOCondition.IN, on_channel_event); } } public bool is_valid () { return fd != -1; } private bool on_channel_event (IOChannel source, IOCondition condition) { read_event (); return true; } private bool read_event () { assert (fd != -1); var event = Linux.RfKillEvent(); var n = sizeof (Linux.RfKillEvent); var bytesread = Posix.read (fd, &event, n); if (bytesread == n) { process_event (event); return true; } return false; } private void process_event (Linux.RfKillEvent event) { // we only want things that affect bluetooth if ((event.type != Linux.RfKillType.ALL) && (event.type != Linux.RfKillType.BLUETOOTH)) return; switch (event.op) { case Linux.RfKillOp.CHANGE: case Linux.RfKillOp.ADD: Entry entry = new Entry (); entry.idx = event.idx; entry.type = event.type; entry.soft = event.soft != 0; entry.hard = event.hard != 0; entries.insert (entry.idx, entry); break; case Linux.RfKillOp.DEL: entries.remove (event.idx); break; default: break; } /* update our blocked property. it should be true if any bluetooth entry is hard- or soft-blocked */ var b = false; foreach (var entry in entries.get_values ()) if ((b = (entry.soft || entry.hard))) break; blocked = b; } }