1 /*
2 SPDX-FileCopyrightText: 2020 MBition GmbH
3 SPDX-FileContributor: Kai Uwe Broulik <kai_uwe.broulik@mbition.io>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7
8 #include "imobiledevice.h"
9
10 #include <QCoreApplication>
11 #include <QScopeGuard>
12
13 #include "imobile_debug.h"
14
15 #include "imobile.h"
16 #include "imobileportablemediaplayer.h"
17
18 #include <libimobiledevice/libimobiledevice.h>
19 #include <libimobiledevice/lockdown.h>
20
21 using namespace Solid::Backends::IMobile;
22
IMobileDevice(const QString & udi)23 IMobileDevice::IMobileDevice(const QString &udi)
24 : Solid::Ifaces::Device()
25 , m_udi(udi)
26 {
27 const QString deviceId = udi.mid(udiPrefix().length() + 1);
28
29 idevice_t device;
30 idevice_new(&device, deviceId.toUtf8().constData());
31 if (!device) {
32 qCWarning(IMOBILE) << "Failed to create device instance for" << deviceId;
33 return;
34 }
35
36 auto deviceCleanup = qScopeGuard([device] {
37 idevice_free(device);
38 });
39
40 lockdownd_client_t lockdowndClient = nullptr;
41 auto ret = lockdownd_client_new(device, &lockdowndClient, "kde_solid_imobile");
42 if (ret != LOCKDOWN_E_SUCCESS || !lockdowndClient) {
43 qCWarning(IMOBILE) << "Failed to create lockdownd client for" << deviceId;
44 return;
45 }
46
47 auto lockdowndClientCleanup = qScopeGuard([lockdowndClient] {
48 lockdownd_client_free(lockdowndClient);
49 });
50
51 char *name = nullptr;
52 auto lockdownRet = lockdownd_get_device_name(lockdowndClient, &name);
53 if (lockdownRet != LOCKDOWN_E_SUCCESS) {
54 qCWarning(IMOBILE) << "Failed to get device name for" << deviceId << lockdownRet;
55 } else if (name) {
56 m_name = QString::fromUtf8(name);
57 free(name);
58 }
59
60 plist_t deviceClassEntry = nullptr;
61 lockdownRet = lockdownd_get_value(lockdowndClient, nullptr /*global domain*/, "DeviceClass", &deviceClassEntry);
62 if (lockdownRet != LOCKDOWN_E_SUCCESS) {
63 qCWarning(IMOBILE) << "Failed to get device class for" << deviceId << lockdownRet;
64 } else {
65 char *deviceClass = nullptr;
66 plist_get_string_val(deviceClassEntry, &deviceClass);
67 if (deviceClass) {
68 m_deviceClass = QString::fromUtf8(deviceClass);
69 free(deviceClass);
70 }
71 }
72 }
73
~IMobileDevice()74 IMobileDevice::~IMobileDevice()
75 {
76 }
77
udi() const78 QString IMobileDevice::udi() const
79 {
80 return m_udi;
81 }
82
parentUdi() const83 QString IMobileDevice::parentUdi() const
84 {
85 return udiPrefix();
86 }
87
vendor() const88 QString IMobileDevice::vendor() const
89 {
90 return QCoreApplication::translate("imobiledevice", "Apple", "Company name");
91 }
92
product() const93 QString IMobileDevice::product() const
94 {
95 // TODO would be nice to use actual product names, e.g. "iPhone 5S"
96 // but accessing device type requires doing a handshake with the device,
97 // which will fail if locked or not paired, and also would require us
98 // to maintain a giant mapping table
99 return m_deviceClass;
100 }
101
icon() const102 QString IMobileDevice::icon() const
103 {
104 if (m_deviceClass.contains(QLatin1String("iPod"))) {
105 return QStringLiteral("multimedia-player-apple-ipod-touch");
106 } else if (m_deviceClass.contains(QLatin1String("iPad"))) {
107 return QStringLiteral("computer-apple-ipad");
108 } else {
109 return QStringLiteral("phone-apple-iphone");
110 }
111 }
112
emblems() const113 QStringList IMobileDevice::emblems() const
114 {
115 return {};
116 }
117
description() const118 QString IMobileDevice::description() const
119 {
120 return m_name;
121 }
122
queryDeviceInterface(const Solid::DeviceInterface::Type & type) const123 bool IMobileDevice::queryDeviceInterface(const Solid::DeviceInterface::Type &type) const
124 {
125 switch (type) {
126 // TODO would be cool to support GenericInterface for reading
127 // arbitrary plist configuration, cf. what ideviceinfo tool does
128
129 case Solid::DeviceInterface::PortableMediaPlayer:
130 return true;
131
132 default:
133 return false;
134 }
135 }
136
createDeviceInterface(const Solid::DeviceInterface::Type & type)137 QObject *IMobileDevice::createDeviceInterface(const Solid::DeviceInterface::Type &type)
138 {
139 if (!queryDeviceInterface(type)) {
140 return nullptr;
141 }
142
143 switch (type) {
144 case Solid::DeviceInterface::PortableMediaPlayer:
145 return new PortableMediaPlayer(this);
146
147 default:
148 Q_UNREACHABLE();
149 return nullptr;
150 }
151 }
152