File indexing completed on 2024-09-15 04:48:48
0001 /* 0002 * SPDX-FileCopyrightText: 2015 David Rosca <nowrep@gmail.com> 0003 * SPDX-FileCopyrightText: 2021 Nate Graham <nate@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "devicemonitor.h" 0009 #include "bluedevil_kded.h" 0010 #include "bluedevildaemon.h" 0011 0012 #include <QTimer> 0013 0014 #include <KConfigGroup> 0015 #include <KDirNotify> 0016 #include <KFilePlacesModel> 0017 0018 #include <BluezQt/Adapter> 0019 #include <BluezQt/Device> 0020 #include <BluezQt/Manager> 0021 #include <BluezQt/Services> 0022 0023 DeviceMonitor::DeviceMonitor(BlueDevilDaemon *daemon) 0024 : QObject(daemon) 0025 , m_manager(daemon->manager()) 0026 , m_isParentValid(true) 0027 , m_config(KSharedConfig::openConfig(QStringLiteral("bluedevilglobalrc"))) 0028 { 0029 Q_FOREACH (BluezQt::AdapterPtr adapter, m_manager->adapters()) { 0030 adapterAdded(adapter); 0031 } 0032 0033 Q_FOREACH (BluezQt::DevicePtr device, m_manager->devices()) { 0034 deviceAdded(device); 0035 } 0036 0037 connect(m_manager, &BluezQt::Manager::adapterAdded, this, &DeviceMonitor::adapterAdded); 0038 connect(m_manager, &BluezQt::Manager::deviceAdded, this, &DeviceMonitor::deviceAdded); 0039 connect(m_manager, &BluezQt::Manager::bluetoothOperationalChanged, this, &DeviceMonitor::bluetoothOperationalChanged); 0040 0041 // Catch suspend/resume events so we can save status when suspending and 0042 // resume when waking up 0043 // It's possible that BlueDevilDaemon has been destroyed, but PrepareForSleep is 0044 // received before DeviceMonitor is destroyed, so a crash will happen. To prevent 0045 // the crash, add a check in login1PrepareForSleep to validate BlueDevilDaemon still exists. 0046 connect( 0047 parent(), 0048 &QObject::destroyed, 0049 this, 0050 [this] { 0051 m_isParentValid = false; 0052 }, 0053 Qt::DirectConnection); 0054 QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.login1"), 0055 QStringLiteral("/org/freedesktop/login1"), 0056 QStringLiteral("org.freedesktop.login1.Manager"), 0057 QStringLiteral("PrepareForSleep"), 0058 this, 0059 SLOT(login1PrepareForSleep(bool))); 0060 0061 // Set initial state 0062 const KConfigGroup globalGroup = m_config->group("Global"); 0063 const QString launchState = globalGroup.readEntry("launchState", "remember"); 0064 if (launchState == QLatin1String("remember")) { 0065 restoreState(); 0066 } else if (launchState == QLatin1String("enable")) { 0067 // Un-block Bluetooth and turn on everything 0068 m_manager->setBluetoothBlocked(false); 0069 for (BluezQt::AdapterPtr adapter : m_manager->adapters()) { 0070 adapter->setPowered(true); 0071 } 0072 } else if (launchState == QLatin1String("disable")) { 0073 // Turn off everything and block Bluetooth 0074 for (BluezQt::AdapterPtr adapter : m_manager->adapters()) { 0075 adapter->setPowered(false); 0076 } 0077 m_manager->setBluetoothBlocked(true); 0078 } 0079 } 0080 0081 KFilePlacesModel *DeviceMonitor::places() 0082 { 0083 if (!m_places) { 0084 m_places = new KFilePlacesModel(this); 0085 } 0086 0087 return m_places; 0088 } 0089 0090 void DeviceMonitor::bluetoothOperationalChanged(bool operational) 0091 { 0092 if (!operational) { 0093 clearPlaces(); 0094 } 0095 } 0096 0097 void DeviceMonitor::adapterAdded(BluezQt::AdapterPtr adapter) 0098 { 0099 // Workaround bluez-qt not registering the powered change after resume from suspend 0100 QTimer::singleShot(1000, this, [this, adapter]() { 0101 restoreAdapter(adapter); 0102 }); 0103 } 0104 0105 void DeviceMonitor::deviceAdded(BluezQt::DevicePtr device) 0106 { 0107 updateDevicePlace(device); 0108 org::kde::KDirNotify::emitFilesAdded(QUrl(QStringLiteral("bluetooth:/"))); 0109 0110 connect(device.data(), &BluezQt::Device::connectedChanged, this, &DeviceMonitor::deviceConnectedChanged); 0111 } 0112 0113 void DeviceMonitor::deviceConnectedChanged(bool connected) 0114 { 0115 Q_UNUSED(connected) 0116 Q_ASSERT(qobject_cast<BluezQt::Device *>(sender())); 0117 0118 BluezQt::DevicePtr device = static_cast<BluezQt::Device *>(sender())->toSharedPtr(); 0119 updateDevicePlace(device); 0120 } 0121 0122 void DeviceMonitor::login1PrepareForSleep(bool active) 0123 { 0124 if (!m_isParentValid) { 0125 return; 0126 } 0127 0128 if (active) { 0129 qCDebug(BLUEDEVIL_KDED_LOG) << "About to suspend"; 0130 saveState(); 0131 } else { 0132 qCDebug(BLUEDEVIL_KDED_LOG) << "About to resume"; 0133 restoreState(); 0134 } 0135 } 0136 0137 void DeviceMonitor::saveState() 0138 { 0139 KConfigGroup adaptersGroup = m_config->group("Adapters"); 0140 KConfigGroup globalGroup = m_config->group("Global"); 0141 0142 if (m_manager->isBluetoothBlocked()) { 0143 globalGroup.writeEntry<bool>("bluetoothBlocked", true); 0144 } else { 0145 globalGroup.deleteEntry("bluetoothBlocked"); 0146 0147 // Save powered state for each adapter 0148 Q_FOREACH (BluezQt::AdapterPtr adapter, m_manager->adapters()) { 0149 const QString key = QStringLiteral("%1_powered").arg(adapter->address()); 0150 adaptersGroup.writeEntry<bool>(key, adapter->isPowered()); 0151 } 0152 } 0153 0154 QStringList connectedDevices; 0155 0156 Q_FOREACH (BluezQt::DevicePtr device, m_manager->devices()) { 0157 if (device->isConnected()) { 0158 connectedDevices.append(device->address()); 0159 } 0160 } 0161 0162 KConfigGroup devicesGroup = m_config->group("Devices"); 0163 devicesGroup.writeEntry<QStringList>(QStringLiteral("connectedDevices"), connectedDevices); 0164 0165 m_config->sync(); 0166 } 0167 0168 void DeviceMonitor::restoreState() 0169 { 0170 KConfigGroup adaptersGroup = m_config->group("Adapters"); 0171 const KConfigGroup globalGroup = m_config->group("Global"); 0172 0173 // Restore blocked/unblocked state 0174 m_manager->setBluetoothBlocked(globalGroup.readEntry<bool>("bluetoothBlocked", false)); 0175 0176 Q_FOREACH (BluezQt::AdapterPtr adapter, m_manager->adapters()) { 0177 const QString key = QStringLiteral("%1_powered").arg(adapter->address()); 0178 adapter->setPowered(adaptersGroup.readEntry<bool>(key, true)); 0179 } 0180 0181 KConfigGroup devicesGroup = m_config->group("Devices"); 0182 const QStringList &connectedDevices = devicesGroup.readEntry<QStringList>(QStringLiteral("connectedDevices"), QStringList()); 0183 0184 for (const QString &addr : connectedDevices) { 0185 BluezQt::DevicePtr device = m_manager->deviceForAddress(addr); 0186 if (device) { 0187 device->connectToDevice(); 0188 } 0189 } 0190 } 0191 0192 void DeviceMonitor::restoreAdapter(BluezQt::AdapterPtr adapter) 0193 { 0194 KConfigGroup adaptersGroup = m_config->group("Adapters"); 0195 0196 const QString &key = QStringLiteral("%1_powered").arg(adapter->address()); 0197 adapter->setPowered(adaptersGroup.readEntry<bool>(key, true)); 0198 } 0199 0200 void DeviceMonitor::clearPlaces() 0201 { 0202 for (int i = 0; i < places()->rowCount(); ++i) { 0203 const QModelIndex &index = places()->index(i, 0); 0204 if (places()->url(index).scheme() == QLatin1String("obexftp")) { 0205 places()->removePlace(index); 0206 i--; 0207 } 0208 } 0209 } 0210 0211 void DeviceMonitor::updateDevicePlace(BluezQt::DevicePtr device) 0212 { 0213 if (!device->uuids().contains(BluezQt::Services::ObexFileTransfer)) { 0214 return; 0215 } 0216 0217 QUrl url; 0218 url.setScheme(QStringLiteral("obexftp")); 0219 url.setHost(device->address().replace(QLatin1Char(':'), QLatin1Char('-'))); 0220 0221 const QModelIndex &index = places()->closestItem(url); 0222 0223 if (device->isConnected()) { 0224 if (places()->url(index) != url) { 0225 qCDebug(BLUEDEVIL_KDED_LOG) << "Adding place" << url; 0226 QString icon = device->icon(); 0227 if (icon == QLatin1String("phone")) { 0228 icon.prepend(QLatin1String("smart")); // Better breeze icon 0229 } 0230 places()->addPlace(device->name(), url, icon); 0231 } 0232 } else { 0233 if (places()->url(index) == url) { 0234 qCDebug(BLUEDEVIL_KDED_LOG) << "Removing place" << url; 0235 places()->removePlace(index); 0236 } 0237 } 0238 } 0239 0240 #include "moc_devicemonitor.cpp"