Merge lp:~jonas-drange/ubuntu-system-settings/hotspots-binding into lp:ubuntu-system-settings
- hotspots-binding
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~jonas-drange/ubuntu-system-settings/hotspots-binding |
Merge into: | lp:ubuntu-system-settings |
Diff against target: |
3076 lines (+2449/-276) 16 files modified
plugins/cellular/CMakeLists.txt (+1/-0) plugins/cellular/Common.qml (+97/-0) plugins/cellular/Components/MultiSim.qml (+7/-2) plugins/cellular/Components/SingleSim.qml (+6/-2) plugins/cellular/Hotspot.qml (+39/-22) plugins/cellular/HotspotSetup.qml (+301/-70) plugins/cellular/hotspotmanager.cpp (+619/-159) plugins/cellular/hotspotmanager.h (+147/-19) plugins/system-update/system_update.cpp (+7/-0) plugins/system-update/system_update.h (+2/-0) plugins/system-update/update_manager.h (+4/-0) tests/autopilot/ubuntu_system_settings/__init__.py (+132/-0) tests/autopilot/ubuntu_system_settings/tests/__init__.py (+62/-1) tests/autopilot/ubuntu_system_settings/tests/networkmanager.py (+902/-0) tests/autopilot/ubuntu_system_settings/tests/test_cellular.py (+122/-1) tests/plugins/system-update/fakesystemupdate.h (+1/-0) |
To merge this branch: | bzr merge lp:~jonas-drange/ubuntu-system-settings/hotspots-binding |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Needs Fixing | ||
PS Jenkins bot | continuous-integration | Approve | |
Mathieu Trudel-Lapierre (community) | Abstain | ||
Matthew Paul Thomas | design | Pending | |
Review via email: mp+252296@code.launchpad.net |
This proposal has been superseded by a proposal from 2015-05-18.
Commit message
[cellular] ap support in binding and rewriting the API to make it easier to interact with
Description of the change
Changes
This modifies the current hotspotmanager binding to do AP and adhoc hotspots, but AP by default. It also mimics [1] in a lot of ways.
So, roughly it
* adds means to talk to URFKill and wpa_supplicant, to respectively soft block/unblock Wi-Fi, and change the interface driver if necessary (which it is on all hybris devices),
* uses NetworkManager events to be less racy,
* and checks for failures and reports them to the user.
The UI has been changed to use the new binding, albeit still hidden due to bug 1426923 bug 1429314 bug 1421671 and bug 1427358.
How to test
If libhybris device (krillin):
1. Edit /etc/dbus-
2. Confirm you can call $ gdbus call --system -d fi.w1.wpa_
All devices:
1. Edit /usr/share/
2. Change 'Exec' line to be Exec=env USS_SHOW_ALL_UI=1 system-settings %u or do $ initctl set-env USS_SHOW_ALL_UI=1
Checklist
* Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)
Yes
* Did you build your software in a clean sbuild/pbuilder chroot or ppa?
Yes
* Did you build your software in a clean sbuild/pbuilder armhf chroot or ppa?
Yes
* Has your component "TestPlan” been executed successfully on emulator, N4?
Yes (Krillin, Arale), not N4 due to bug from above.
* Has a 5 minute exploratory testing run been executed on N4?
No, see above.
* If you changed the packaging (debian), did you subscribe a core-dev to this MP?
N/A
* If you changed the UI, did you subscribe the design-reviewers to this MP?
Yes (mpt)
* What components might get impacted by your changes?
Cellular for Single and Multi-SIM
* Have you requested review by the teams of these owning components?
N/A
[1] http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1339
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1354
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1359
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
See a couple comments and questions inline.
Jonas G. Drange (jonas-drange) wrote : | # |
Information given, but sat MP back to WIP to work on what Ken pointed out.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1362
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1363
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
In my testing I found that the ssidField which needs to be 8 or more characters only becomes valid when you enter the 9th character or the field loses focus. This MP is more about the backend it needs and the UI is still hidden. Not sure we want to block on that. However the hotspot I created on my mako appeared to work, but I couldn't make a successful connection to it from my laptop.
Mathieu Trudel-Lapierre (cyphermox) wrote : | # |
I only reviewed the backend code but it looks fine to me; see one minor inline comment about wifi security.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1364
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1365
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
Looks good, I pushed a fix to show the UI and disable predictive text in the password to another branch
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1368
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1369. By Jonas G. Drange
-
style
- 1370. By Jonas G. Drange
-
skip if wifi disabled
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1370
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
Please fix the indenting of the .cpp and .h files, you used 2 spaces instead of 4. Also see the comments inline in the diff.
- 1371. By Jonas G. Drange
-
start addressing ken's comments
- 1372. By Jonas G. Drange
-
four tests, fixing setup and teardown
- 1373. By Jonas G. Drange
-
fixing some test imports, indent now 4 spaces
- 1374. By Jonas G. Drange
-
merge trunk
- 1375. By Jonas G. Drange
-
merge prereq branch
- 1376. By Jonas G. Drange
-
hide if mako
- 1377. By Jonas G. Drange
-
add comment to import
- 1378. By Jonas G. Drange
-
hiding completely on mako
- 1379. By Jonas G. Drange
-
unhide for testing
- 1380. By Jonas G. Drange
-
remove enabledshim
- 1381. By Jonas G. Drange
-
using usc serverpropertys
ynchroniser - 1382. By Jonas G. Drange
-
foo
- 1383. By Jonas G. Drange
-
let the checkbox render before starting hotspot
- 1384. By Jonas G. Drange
-
disable back button so as to ensure that the wifi is soft unblocked
- 1385. By Jonas G. Drange
-
sync trunk
- 1386. By Jonas G. Drange
-
sync with trunk
- 1387. By Jonas G. Drange
-
sync with trunk
Unmerged revisions
Preview Diff
1 | === modified file 'plugins/cellular/CMakeLists.txt' |
2 | --- plugins/cellular/CMakeLists.txt 2015-03-26 21:57:53 +0000 |
3 | +++ plugins/cellular/CMakeLists.txt 2015-05-18 15:06:15 +0000 |
4 | @@ -6,6 +6,7 @@ |
5 | set(QML_SOURCES |
6 | apn.js |
7 | carriers.js |
8 | + Common.qml |
9 | CustomApnEditor.qml |
10 | PageChooseApn.qml |
11 | PageChooseCarrier.qml |
12 | |
13 | === added file 'plugins/cellular/Common.qml' |
14 | --- plugins/cellular/Common.qml 1970-01-01 00:00:00 +0000 |
15 | +++ plugins/cellular/Common.qml 2015-05-18 15:06:15 +0000 |
16 | @@ -0,0 +1,97 @@ |
17 | +/* |
18 | + * Copyright (C) 2015 Canonical Ltd |
19 | + * |
20 | + * This program is free software: you can redistribute it and/or modify |
21 | + * it under the terms of the GNU General Public License version 3 as |
22 | + * published by the Free Software Foundation. |
23 | + * |
24 | + * This program is distributed in the hope that it will be useful, |
25 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 | + * GNU General Public License for more details. |
28 | + * |
29 | + * You should have received a copy of the GNU General Public License |
30 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
31 | + * |
32 | + * Authors: |
33 | + * Jonas G. Drange <jonas.drange@canonical.com> |
34 | + * |
35 | +*/ |
36 | +import QtQuick 2.0 |
37 | + |
38 | +Item { |
39 | + /* |
40 | + The mapping of code to string is taken from |
41 | + http://bazaar.launchpad.net/~vcs-imports/ |
42 | + network-manager/trunk/view/head:/cli/src/common.c |
43 | + |
44 | + NetworkManager documentation: https://developer.gnome.org/ |
45 | + NetworkManager/0.9/spec.html#type-NM_DEVICE_STATE_REASON |
46 | + */ |
47 | + function reasonToString (reason) { |
48 | + switch (reason) { |
49 | + case 0: |
50 | + return i18n.tr("Unknown error"); |
51 | + case 1: |
52 | + return i18n.tr("No reason given"); |
53 | + case 2: |
54 | + return i18n.tr("Device is now managed"); |
55 | + case 3: |
56 | + return i18n.tr("Device is now unmanaged"); |
57 | + case 4: |
58 | + return i18n.tr("The device could not be readied for configuration"); |
59 | + case 5: |
60 | + return i18n.tr("IP configuration could not be reserved (no available address, timeout, etc.)"); |
61 | + case 6: |
62 | + return i18n.tr("The IP configuration is no longer valid"); |
63 | + case 7: |
64 | + return i18n.tr("Your authentication details were incorrect"); |
65 | + case 8: |
66 | + return i18n.tr("802.1X supplicant disconnected"); |
67 | + case 9: |
68 | + return i18n.tr("802.1X supplicant configuration failed"); |
69 | + case 10: |
70 | + return i18n.tr("802.1X supplicant failed"); |
71 | + case 11: |
72 | + return i18n.tr("802.1X supplicant took too long to authenticate"); |
73 | + case 15: |
74 | + return i18n.tr("DHCP client failed to start"); |
75 | + case 16: |
76 | + return i18n.tr("DHCP client error"); |
77 | + case 17: |
78 | + return i18n.tr("DHCP client failed"); |
79 | + case 18: |
80 | + return i18n.tr("Shared connection service failed to start"); |
81 | + case 19: |
82 | + return i18n.tr("Shared connection service failed"); |
83 | + case 35: |
84 | + return i18n.tr("Necessary firmware for the device may be missing"); |
85 | + case 36: |
86 | + return i18n.tr("The device was removed"); |
87 | + case 37: |
88 | + return i18n.tr("NetworkManager went to sleep"); |
89 | + case 38: |
90 | + return i18n.tr("The device's active connection disappeared"); |
91 | + case 39: |
92 | + return i18n.tr("Device disconnected by user or client"); |
93 | + case 41: |
94 | + return i18n.tr("The device's existing connection was assumed"); |
95 | + case 42: |
96 | + return i18n.tr("The supplicant is now available"); |
97 | + case 43: |
98 | + return i18n.tr("The modem could not be found"); |
99 | + case 44: |
100 | + return i18n.tr("The Bluetooth connection failed or timed out"); |
101 | + case 50: |
102 | + return i18n.tr("A dependency of the connection failed"); |
103 | + case 52: |
104 | + return i18n.tr("ModemManager is unavailable"); |
105 | + case 53: |
106 | + return i18n.tr("The Wi-Fi network could not be found"); |
107 | + case 54: |
108 | + return i18n.tr("A secondary connection of the base connection failed"); |
109 | + default: |
110 | + return i18n.tr("Unknown"); |
111 | + } |
112 | + } |
113 | +} |
114 | |
115 | === modified file 'plugins/cellular/Components/MultiSim.qml' |
116 | --- plugins/cellular/Components/MultiSim.qml 2015-01-21 20:44:20 +0000 |
117 | +++ plugins/cellular/Components/MultiSim.qml 2015-05-18 15:06:15 +0000 |
118 | @@ -22,6 +22,7 @@ |
119 | import SystemSettings 1.0 |
120 | import Ubuntu.Components 0.1 |
121 | import Ubuntu.Components.ListItems 0.1 as ListItem |
122 | +import Ubuntu.SystemSettings.Update 1.0 |
123 | |
124 | Column { |
125 | objectName: "multiSim" |
126 | @@ -58,9 +59,13 @@ |
127 | text: i18n.tr("Wi-Fi hotspot") |
128 | progression: true |
129 | onClicked: { |
130 | - pageStack.push(Qt.resolvedUrl("Hotspot.qml")) |
131 | + pageStack.push(Qt.resolvedUrl("../Hotspot.qml")) |
132 | } |
133 | - visible: showAllUI && (actionGroup.actionObject.valid ? actionGroup.actionObject.state : false) |
134 | + visible: (showAllUI && |
135 | + (actionGroup.actionObject.valid ? |
136 | + actionGroup.actionObject.state : false) && |
137 | + UpdateManager.deviceName !== "mako") |
138 | + |
139 | } |
140 | |
141 | ListItem.Standard { |
142 | |
143 | === modified file 'plugins/cellular/Components/SingleSim.qml' |
144 | --- plugins/cellular/Components/SingleSim.qml 2015-01-20 12:23:34 +0000 |
145 | +++ plugins/cellular/Components/SingleSim.qml 2015-05-18 15:06:15 +0000 |
146 | @@ -67,12 +67,16 @@ |
147 | |
148 | ListItem.SingleValue { |
149 | id: hotspotItem |
150 | + objectName: "hotspotEntry" |
151 | text: i18n.tr("Wi-Fi hotspot") |
152 | progression: true |
153 | onClicked: { |
154 | - pageStack.push(Qt.resolvedUrl("Hotspot.qml")) |
155 | + pageStack.push(Qt.resolvedUrl("../Hotspot.qml")) |
156 | } |
157 | - visible: showAllUI && (actionGroup.actionObject.valid ? actionGroup.actionObject.state : false) |
158 | + visible: (showAllUI && |
159 | + (actionGroup.actionObject.valid ? |
160 | + actionGroup.actionObject.state : false) && |
161 | + UpdateManager.deviceName !== "mako") |
162 | } |
163 | |
164 | ListItem.Standard { |
165 | |
166 | === modified file 'plugins/cellular/Hotspot.qml' |
167 | --- plugins/cellular/Hotspot.qml 2014-07-29 15:39:08 +0000 |
168 | +++ plugins/cellular/Hotspot.qml 2015-05-18 15:06:15 +0000 |
169 | @@ -21,56 +21,73 @@ |
170 | import Ubuntu.Components 0.1 |
171 | import Ubuntu.Components.ListItems 0.1 as ListItem |
172 | import Ubuntu.SystemSettings.Cellular 1.0 |
173 | +import Ubuntu.Components.Popups 0.1 |
174 | |
175 | ItemPage { |
176 | |
177 | id: hotspot |
178 | + objectName: "hotspotPage" |
179 | |
180 | title: i18n.tr("Wi-Fi hotspot") |
181 | |
182 | HotspotManager { |
183 | id: hotspotManager |
184 | + // TODO(jgdx): Figure out why serverCheckedChanged is not fired |
185 | + // automatically whenever hotspotManager.enabled changes. |
186 | + onEnabledChanged: { |
187 | + hotspotSwitch.serverChecked = enabled; |
188 | + hotspotSwitch.serverCheckedChanged(); |
189 | + } |
190 | + } |
191 | + |
192 | + Loader { |
193 | + id: setup |
194 | + asynchronous: false |
195 | } |
196 | |
197 | Column { |
198 | |
199 | anchors.fill: parent |
200 | + spacing: units.gu(2) |
201 | |
202 | ListItem.Standard { |
203 | text: i18n.tr("Hotspot") |
204 | + enabled: hotspotManager.stored |
205 | control: Switch { |
206 | id: hotspotSwitch |
207 | - checked: hotspotManager.isHotspotActive() |
208 | - onTriggered: { |
209 | - if(checked) { |
210 | - hotspotManager.enableHotspot() |
211 | - } else { |
212 | - hotspotManager.disableHotspot() |
213 | - } |
214 | - } |
215 | + objectName: "hotspotSwitch" |
216 | + property bool serverChecked: hotspotManager.enabled |
217 | + onServerCheckedChanged: checked = serverChecked |
218 | + Component.onCompleted: checked = serverChecked |
219 | + onTriggered: hotspotManager.enabled = checked |
220 | } |
221 | } |
222 | |
223 | - Label { |
224 | - width: parent.width |
225 | - wrapMode: Text.WordWrap |
226 | - anchors.leftMargin: units.gu(2) |
227 | - anchors.rightMargin: units.gu(2) |
228 | - text : hotspotSwitch.enabled ? |
229 | + ListItem.Caption { |
230 | + anchors { |
231 | + left: parent.left |
232 | + right: parent.right |
233 | + leftMargin: units.gu(2) |
234 | + rightMargin: units.gu(2) |
235 | + } |
236 | + text : hotspotSwitch.stored ? |
237 | i18n.tr("When hotspot is on, other devices can use your cellular data connection over Wi-Fi. Normal data charges apply.") |
238 | - : i18n.tr("Other devices can use your cellular data connection over the Wi-Fi network. Normal data charges apply.") |
239 | + : i18n.tr("Other devices can use your cellular data connection over the Wi-Fi network. Normal data charges apply.") |
240 | } |
241 | |
242 | Button { |
243 | - text: i18n.tr("Set up hotspot") |
244 | - anchors.left: parent.left |
245 | - anchors.right: parent.right |
246 | - anchors.leftMargin: units.gu(2) |
247 | - anchors.rightMargin: units.gu(2) |
248 | + objectName: "hotspotSetupEntry" |
249 | + anchors.horizontalCenter: parent.horizontalCenter |
250 | + width: parent.width - units.gu(4) |
251 | + text: hotspotManager.stored ? |
252 | + i18n.tr("Change password/setup…") : i18n.tr("Set up hotspot…") |
253 | + |
254 | onClicked: { |
255 | - pageStack.push(Qt.resolvedUrl("HotspotSetup.qml"), {hotspotManager: hotspotManager}) |
256 | + setup.setSource(Qt.resolvedUrl("HotspotSetup.qml")); |
257 | + PopupUtils.open(setup.item, hotspot, { |
258 | + hotspotManager: hotspotManager |
259 | + }); |
260 | } |
261 | } |
262 | - |
263 | } |
264 | } |
265 | |
266 | === modified file 'plugins/cellular/HotspotSetup.qml' |
267 | --- plugins/cellular/HotspotSetup.qml 2014-07-04 11:32:33 +0000 |
268 | +++ plugins/cellular/HotspotSetup.qml 2015-05-18 15:06:15 +0000 |
269 | @@ -22,82 +22,313 @@ |
270 | import Ubuntu.Components 0.1 |
271 | import Ubuntu.Components.ListItems 0.1 as ListItem |
272 | import Ubuntu.SystemSettings.Cellular 1.0 |
273 | - |
274 | -ItemPage { |
275 | - |
276 | +import Ubuntu.Components.Popups 0.1 |
277 | + |
278 | +Component { |
279 | id: hotspotSetup |
280 | - title: i18n.tr("Change hotspot setup") |
281 | - |
282 | - property var hotspotManager: null |
283 | - |
284 | - |
285 | - Column { |
286 | - |
287 | - anchors.fill: parent |
288 | - |
289 | - ListItem.Standard { |
290 | - id: ssidLabel |
291 | - text: i18n.tr("Hotspot name") |
292 | - } |
293 | - |
294 | - TextField { |
295 | - id: ssidField |
296 | - text: hotspotManager.getHotspotName() |
297 | - anchors.left: parent.left |
298 | - anchors.right: parent.right |
299 | - anchors.leftMargin: units.gu(2) |
300 | - anchors.rightMargin: units.gu(2) |
301 | - } |
302 | - |
303 | - ListItem.Standard { |
304 | - id: passwordLabel |
305 | - text: i18n.tr("Key (must be 8 characters or longer)") |
306 | - } |
307 | - |
308 | - TextField { |
309 | - id: passwordField |
310 | - text: hotspotManager.getHotspotPassword() |
311 | - echoMode: passwordVisibleSwitch.checked ? TextInput.Normal : TextInput.Password |
312 | - anchors.left: parent.left |
313 | - anchors.right: parent.right |
314 | - anchors.leftMargin: units.gu(2) |
315 | - anchors.rightMargin: units.gu(2) |
316 | - } |
317 | - |
318 | - ListItem.Standard { |
319 | - text: i18n.tr("Show key") |
320 | - id: passwordVisible |
321 | - control: Switch { |
322 | - id: passwordVisibleSwitch |
323 | + |
324 | + Dialog { |
325 | + id: hotspotSetupDialog |
326 | + property var hotspotManager: null |
327 | + |
328 | + /* hotspotManager.stored changes as soon as the user has added a |
329 | + hotspot, and we use this value when we choose between e.g. "Change" and |
330 | + "Setup". We'd like the narrative to be consistent, so we stick with |
331 | + what the stored value was at component completion. |
332 | + */ |
333 | + property bool stored: false |
334 | + Component.onCompleted: stored = hotspotManager.stored; |
335 | + |
336 | + objectName: "hotspotSetup" |
337 | + anchorToKeyboard: true |
338 | + |
339 | + function settingsValid() { |
340 | + return ssidField.text != "" && passwordField.length >= 8; |
341 | + } |
342 | + |
343 | + title: stored ? |
344 | + i18n.tr("Change hotspot setup") : i18n.tr("Setup hotspot") |
345 | + text: feedback.enabled ? feedback.text : ""; |
346 | + |
347 | + Common { |
348 | + id: common |
349 | + } |
350 | + |
351 | + states: [ |
352 | + State { |
353 | + name: "STARTING" |
354 | + PropertyChanges { |
355 | + target: workingIndicator |
356 | + running: true |
357 | + } |
358 | + PropertyChanges { |
359 | + target: ssidLabel |
360 | + opacity: 0.5 |
361 | + } |
362 | + PropertyChanges { |
363 | + target: ssidField |
364 | + enabled: false |
365 | + } |
366 | + PropertyChanges { |
367 | + target: passwordLabel |
368 | + opacity: 0.5 |
369 | + } |
370 | + PropertyChanges { |
371 | + target: passwordField |
372 | + enabled: false |
373 | + } |
374 | + PropertyChanges { |
375 | + target: feedback |
376 | + enabled: true |
377 | + } |
378 | + PropertyChanges { |
379 | + target: confirmButton |
380 | + enabled: false |
381 | + } |
382 | + }, |
383 | + |
384 | + State { |
385 | + name: "FAILED" |
386 | + PropertyChanges { |
387 | + target: feedback |
388 | + enabled: true |
389 | + } |
390 | + PropertyChanges { |
391 | + target: ssidField |
392 | + errorHighlight: true |
393 | + } |
394 | + StateChangeScript { |
395 | + script: ssidField.forceActiveFocus() |
396 | + } |
397 | + }, |
398 | + |
399 | + State { |
400 | + name: "SUCCEEDED" |
401 | + PropertyChanges { |
402 | + target: successIcon |
403 | + visible: true |
404 | + } |
405 | + PropertyChanges { |
406 | + target: successIndicator |
407 | + running: true |
408 | + } |
409 | + PropertyChanges { |
410 | + target: ssidLabel |
411 | + opacity: 0.5 |
412 | + } |
413 | + PropertyChanges { |
414 | + target: ssidField |
415 | + enabled: false |
416 | + } |
417 | + PropertyChanges { |
418 | + target: passwordLabel |
419 | + opacity: 0.5 |
420 | + } |
421 | + PropertyChanges { |
422 | + target: passwordField |
423 | + enabled: false |
424 | + } |
425 | + PropertyChanges { |
426 | + target: confirmButton |
427 | + enabled: false |
428 | + } |
429 | } |
430 | - } |
431 | + ] |
432 | |
433 | - RowLayout { |
434 | + Column { |
435 | anchors { |
436 | left: parent.left |
437 | right: parent.right |
438 | - margins: units.gu(2) |
439 | - } |
440 | - |
441 | - Button { |
442 | - id: cancelButton |
443 | - Layout.fillWidth: true |
444 | - text: i18n.tr("Cancel") |
445 | - onClicked: { |
446 | - pageStack.pop() |
447 | - } |
448 | - } |
449 | - |
450 | - Button { |
451 | - id: connectButton |
452 | - Layout.fillWidth: true |
453 | - text: i18n.tr("Change") |
454 | - enabled: ssidField.text != "" && passwordField.length >= 8 |
455 | - onClicked: { |
456 | - hotspotManager.setupHotspot(ssidField.text, passwordField.text) |
457 | - pageStack.pop() |
458 | - } |
459 | - } |
460 | + } |
461 | + spacing: units.gu(1) |
462 | + |
463 | + Label { |
464 | + property bool enabled: false |
465 | + id: feedback |
466 | + horizontalAlignment: Text.AlignHCenter |
467 | + height: contentHeight |
468 | + wrapMode: Text.WordWrap |
469 | + visible: false |
470 | + } |
471 | + |
472 | + Label { |
473 | + id: ssidLabel |
474 | + text: i18n.tr("Hotspot name") |
475 | + fontSize: "medium" |
476 | + font.bold: true |
477 | + color: Theme.palette.selected.backgroundText |
478 | + elide: Text.ElideRight |
479 | + } |
480 | + |
481 | + TextField { |
482 | + id: ssidField |
483 | + objectName: "ssidField" |
484 | + text: hotspotManager.ssid |
485 | + inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText |
486 | + Component.onCompleted: forceActiveFocus() |
487 | + width: parent.width |
488 | + } |
489 | + |
490 | + Label { |
491 | + id: passwordLabel |
492 | + text: i18n.tr("Key (must be 8 characters or longer)") |
493 | + fontSize: "medium" |
494 | + font.bold: true |
495 | + color: Theme.palette.selected.backgroundText |
496 | + wrapMode: Text.WordWrap |
497 | + width: parent.width |
498 | + } |
499 | + |
500 | + TextField { |
501 | + id: passwordField |
502 | + objectName: "passwordField" |
503 | + text: hotspotManager.password |
504 | + echoMode: passwordVisibleSwitch.checked ? |
505 | + TextInput.Normal : TextInput.Password |
506 | + inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText |
507 | + width: parent.width |
508 | + } |
509 | + |
510 | + ListItem.Standard { |
511 | + text: i18n.tr("Show key") |
512 | + id: passwordVisible |
513 | + onClicked: passwordVisibleSwitch.trigger() |
514 | + control: Switch { |
515 | + id: passwordVisibleSwitch |
516 | + activeFocusOnPress: false |
517 | + } |
518 | + } |
519 | + |
520 | + Row { |
521 | + |
522 | + anchors { |
523 | + left: parent.left |
524 | + right: parent.right |
525 | + } |
526 | + width: parent.width |
527 | + spacing: units.gu(2) |
528 | + |
529 | + Button { |
530 | + id: cancelButton |
531 | + width: (parent.width / 2) - units.gu(1) |
532 | + text: i18n.tr("Cancel") |
533 | + activeFocusOnPress: false |
534 | + onClicked: PopupUtils.close(hotspotSetupDialog) |
535 | + } |
536 | + |
537 | + Button { |
538 | + id: confirmButton |
539 | + objectName: "confirmButton" |
540 | + width: (parent.width / 2) - units.gu(1) |
541 | + text: hotspotSetupDialog.stored ? i18n.tr("Change") : |
542 | + i18n.tr("Enable") |
543 | + enabled: settingsValid() |
544 | + activeFocusOnPress: false |
545 | + onClicked: { |
546 | + if (hotspotSetupDialog.stored) { |
547 | + changeAction.trigger() |
548 | + } else { |
549 | + enableAction.trigger(); |
550 | + } |
551 | + } |
552 | + |
553 | + Icon { |
554 | + id: successIcon |
555 | + anchors.centerIn: parent |
556 | + height: parent.height - units.gu(1.5) |
557 | + width: parent.height - units.gu(1.5) |
558 | + name: "tick" |
559 | + color: "green" |
560 | + visible: false |
561 | + } |
562 | + |
563 | + ActivityIndicator { |
564 | + id: workingIndicator |
565 | + anchors.centerIn: parent |
566 | + running: false |
567 | + visible: running |
568 | + height: parent.height - units.gu(1.5) |
569 | + } |
570 | + } |
571 | + } |
572 | + } |
573 | + |
574 | + Action { |
575 | + id: enableAction |
576 | + enabled: settingsValid() |
577 | + onTriggered: { |
578 | + hotspotSetupDialog.state = "STARTING"; |
579 | + |
580 | + function hotspotEnabledHandler (enabled) { |
581 | + if (enabled) { |
582 | + hotspotSetupDialog.state = "SUCCEEDED"; |
583 | + hotspotManager.enabledChanged.disconnect( |
584 | + hotspotEnabledHandler); |
585 | + } |
586 | + } |
587 | + |
588 | + hotspotManager.ssid = ssidField.text; |
589 | + hotspotManager.password = passwordField.text; |
590 | + hotspotManager.enabledChanged.connect(hotspotEnabledHandler); |
591 | + hotspotManager.enabled = true; |
592 | + } |
593 | + } |
594 | + |
595 | + Action { |
596 | + id: changeAction |
597 | + enabled: settingsValid() |
598 | + onTriggered: { |
599 | + |
600 | + function hotspotEnabledHandler (enabled) { |
601 | + if (enabled) { |
602 | + hotspotSetupDialog.state = "SUCCEEDED"; |
603 | + hotspotManager.enabledChanged.disconnect( |
604 | + hotspotEnabledHandler); |
605 | + } |
606 | + } |
607 | + |
608 | + function hotspotDisabledHandler (enabled) { |
609 | + if (!enabled) { |
610 | + hotspotManager.enabledChanged.connect(hotspotEnabledHandler); |
611 | + hotspotManager.enabled = true; |
612 | + hotspotManager.enabledChanged.disconnect( |
613 | + hotspotDisabledHandler); |
614 | + } |
615 | + } |
616 | + |
617 | + hotspotManager.ssid = ssidField.text; |
618 | + hotspotManager.password = passwordField.text; |
619 | + |
620 | + if (hotspotManager.enabled) { |
621 | + hotspotSetupDialog.state = "STARTING"; |
622 | + hotspotManager.enabledChanged.connect( |
623 | + hotspotDisabledHandler); |
624 | + hotspotManager.enabled = false; |
625 | + } else { |
626 | + PopupUtils.close(hotspotSetupDialog); |
627 | + } |
628 | + } |
629 | + } |
630 | + |
631 | + /* Timer that shows a tick in the connect button once we have |
632 | + successfully changed/started a hotspot. */ |
633 | + Timer { |
634 | + id: successIndicator |
635 | + interval: 2000 |
636 | + running: false |
637 | + repeat: false |
638 | + onTriggered: PopupUtils.close(hotspotSetupDialog) |
639 | + } |
640 | + |
641 | + Connections { |
642 | + target: hotspotManager |
643 | + |
644 | + onReportError: { |
645 | + if (hotspotSetupDialog.state === "STARTING") { |
646 | + hotspotSetupDialog.state = "FAILED"; |
647 | + feedback.text = common.reasonToString(reason); |
648 | + } |
649 | + } |
650 | } |
651 | } |
652 | } |
653 | |
654 | === modified file 'plugins/cellular/hotspotmanager.cpp' |
655 | --- plugins/cellular/hotspotmanager.cpp 2014-07-23 13:44:31 +0000 |
656 | +++ plugins/cellular/hotspotmanager.cpp 2015-05-18 15:06:15 +0000 |
657 | @@ -1,8 +1,9 @@ |
658 | /* |
659 | - * Copyright (C) 2014 Canonical, Ltd. |
660 | + * Copyright (C) 2014, 2015 Canonical, Ltd. |
661 | * |
662 | * Authors: |
663 | * Jussi Pakkanen <jussi.pakkanen@canonical.com> |
664 | + * Jonas G. Drange <jonas.drange@canonical.com> |
665 | * |
666 | * This program is free software: you can redistribute it and/or modify it |
667 | * under the terms of the GNU General Public License version 3, as published |
668 | @@ -15,13 +16,9 @@ |
669 | * |
670 | * You should have received a copy of the GNU General Public License |
671 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
672 | - */ |
673 | +*/ |
674 | |
675 | #include "hotspotmanager.h" |
676 | - |
677 | -#include "nm_manager_proxy.h" |
678 | -#include "nm_settings_proxy.h" |
679 | -#include "nm_settings_connection_proxy.h" |
680 | #include <QStringList> |
681 | #include <QDBusReply> |
682 | #include <QtDebug> |
683 | @@ -31,31 +28,156 @@ |
684 | typedef QMap<QString, QVariantMap> nmConnectionArg; |
685 | Q_DECLARE_METATYPE(nmConnectionArg) |
686 | |
687 | -namespace { |
688 | +namespace { // wpa_supplicant interaction |
689 | + |
690 | +const QString wpa_supplicant_service("fi.w1.wpa_supplicant1"); |
691 | +const QString wpa_supplicant_interface("fi.w1.wpa_supplicant1"); |
692 | +const QString wpa_supplicant_path("/fi/w1/wpa_supplicant1"); |
693 | + |
694 | +bool isHybrisWlan() { |
695 | + QString program("getprop"); |
696 | + QStringList arguments; |
697 | + arguments << "urfkill.hybris.wlan"; |
698 | + |
699 | + QProcess *getprop = new QProcess(); |
700 | + getprop->start(program, arguments); |
701 | + |
702 | + if (!getprop->waitForFinished()) { |
703 | + qCritical() << "getprop process failed:" << getprop->errorString(); |
704 | + delete getprop; |
705 | + return false; |
706 | + } |
707 | + |
708 | + int index = getprop->readAllStandardOutput().indexOf("1"); |
709 | + delete getprop; |
710 | + |
711 | + // A non-negative integer means getprop returned 1 |
712 | + return index >= 0; |
713 | +} |
714 | + |
715 | +// True if changed successfully, or there was no need. Otherwise false. |
716 | +// Supported modes are 'p2p', 'sta' and 'ap'. |
717 | +bool changeInterfaceFirmware(const QString interface, const QString mode) { |
718 | + // Not supported. |
719 | + if (mode == "adhoc") { |
720 | + return true; |
721 | + } |
722 | + |
723 | + if (isHybrisWlan()) { |
724 | + QDBusInterface wpasIface ( |
725 | + wpa_supplicant_service, wpa_supplicant_path, wpa_supplicant_interface, |
726 | + QDBusConnection::systemBus()); |
727 | + |
728 | + const QDBusObjectPath interface_path(interface); |
729 | + |
730 | + // TODO(jgdx): We need to guard against calling this |
731 | + // when the interface is not soft blocked. |
732 | + auto set_interface = wpasIface.call("SetInterfaceFirmware", |
733 | + QVariant::fromValue(interface_path), QVariant(mode)); |
734 | + |
735 | + if (set_interface.type() == QDBusMessage::ErrorMessage) { |
736 | + qCritical() << "Failed to change interface firmware:" |
737 | + << set_interface.errorMessage(); |
738 | + return false; |
739 | + } else { |
740 | + return true; |
741 | + } |
742 | + } |
743 | + |
744 | + // We had no need to change the firmware. |
745 | + return true; |
746 | +} |
747 | +} // wpa_supplicant interaction |
748 | + |
749 | +namespace { // UrfKill interaction |
750 | + |
751 | +const QString urfkill_service("org.freedesktop.URfkill"); |
752 | +const QString urfkill_interface("org.freedesktop.URfkill"); |
753 | +const QString urfkill_path("/org/freedesktop/URfkill"); |
754 | + |
755 | +// True if call went through and returned true. |
756 | +bool setWifiBlock(bool block) { |
757 | + QDBusInterface urfkill_dbus_interface(urfkill_service, urfkill_path, |
758 | + urfkill_interface, QDBusConnection::systemBus()); |
759 | + |
760 | + const unsigned int device_type = 1; /* wifi type */ |
761 | + auto reply = urfkill_dbus_interface.call("Block", device_type, block); |
762 | + |
763 | + if (reply.type() == QDBusMessage::ErrorMessage) { |
764 | + qCritical() << "Failed to block wifi" << reply.errorMessage(); |
765 | + return false; |
766 | + } |
767 | + |
768 | + // We got exactly one argument back, and it's not an error. |
769 | + if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 1) { |
770 | + // returned false from call |
771 | + if (!qdbus_cast<bool>(reply.arguments().at(0))) { |
772 | + qCritical() << "URfkill Block call did not succeed"; |
773 | + return false; |
774 | + } else { |
775 | + return true; |
776 | + } |
777 | + } |
778 | + // We did not get what we wanted from dbus, so we treat that as failure. |
779 | + return false; |
780 | +} |
781 | +} // UrfKill interaction |
782 | + |
783 | +namespace { // NetworkManager interaction |
784 | |
785 | const QString nm_service("org.freedesktop.NetworkManager"); |
786 | +const QString nm_object("/org/freedesktop/NetworkManager"); |
787 | const QString nm_settings_object("/org/freedesktop/NetworkManager/Settings"); |
788 | const QString nm_settings_interface("org.freedesktop.NetworkManager.Settings"); |
789 | const QString nm_connection_interface("org.freedesktop.NetworkManager.Settings.Connection"); |
790 | -const QString nm_dbus_path("/org/freedesktop/NetworkManager"); |
791 | +const QString nm_connection_active_interface("org.freedesktop.NetworkManager.Connection.Active"); |
792 | const QString nm_device_interface("org.freedesktop.NetworkManager.Device"); |
793 | - |
794 | -#define NM_METHOD_NAME "AddAndActivateConnection" |
795 | - |
796 | -void startAdhoc(const QByteArray &ssid, const QString &password, const QDBusObjectPath &devicePath) { |
797 | +const QString dbus_properties_interface("org.freedesktop.DBus.Properties"); |
798 | +const QString connection_property("Connection"); |
799 | + |
800 | +// NetworkManager dbus interface proxy we will use to query |
801 | +// against NetworkManager. See |
802 | +// https://developer.gnome.org/NetworkManager/0.9/spec.html |
803 | +// #org.freedesktop.NetworkManager |
804 | +OrgFreedesktopNetworkManagerInterface nm_manager( |
805 | + nm_service, nm_object, QDBusConnection::systemBus()); |
806 | + |
807 | +// NetworkManager Settings interface proxy we use to get |
808 | +// the list of connections, as well as adding connections. |
809 | +// See https://developer.gnome.org/NetworkManager/0.9/spec.html |
810 | +// #org.freedesktop.NetworkManager.Settings |
811 | +OrgFreedesktopNetworkManagerSettingsInterface nm_settings( |
812 | + nm_service, nm_settings_object, QDBusConnection::systemBus()); |
813 | + |
814 | +// Helper that maps QStrings to other QVariantMaps, i.e. |
815 | +// QMap<QString, QVariantMap>. QVariantMap is an alias for |
816 | +// QMap<QString, QVariant>. |
817 | +// See http://doc.qt.io/qt-5/qvariant.html#QVariantMap-typedef and |
818 | +// https://developer.gnome.org/NetworkManager/0.9/spec.html |
819 | +// #type-String_String_Variant_Map_Map |
820 | +nmConnectionArg createConnectionSettings( |
821 | + const QByteArray &ssid, const QString &password, |
822 | + const QDBusObjectPath &devicePath, QString mode, bool autoConnect = true) { |
823 | + Q_UNUSED(devicePath); |
824 | nmConnectionArg connection; |
825 | |
826 | - QDBusObjectPath specific("/"); |
827 | + QString s_ssid = QString::fromLatin1(ssid); |
828 | + QString s_uuid = QUuid().createUuid().toString(); |
829 | + // Remove {} from the generated uuid. |
830 | + s_uuid.remove(0, 1); |
831 | + s_uuid.remove(s_uuid.size() - 1, 1); |
832 | |
833 | QVariantMap wireless; |
834 | wireless[QStringLiteral("security")] = QVariant(QStringLiteral("802-11-wireless-security")); |
835 | wireless[QStringLiteral("ssid")] = QVariant(ssid); |
836 | - wireless[QStringLiteral("mode")] = QVariant(QStringLiteral("adhoc")); |
837 | + wireless[QStringLiteral("mode")] = QVariant(mode); |
838 | + |
839 | connection["802-11-wireless"] = wireless; |
840 | |
841 | QVariantMap connsettings; |
842 | - connsettings[QStringLiteral("autoconnect")] = QVariant(false); |
843 | - connsettings[QStringLiteral("uuid")] = QVariant(QStringLiteral("aab22b5d-7342-48dc-8920-1b7da31d6829")); |
844 | + connsettings[QStringLiteral("autoconnect")] = QVariant(autoConnect); |
845 | + connsettings[QStringLiteral("id")] = QVariant(s_ssid); |
846 | + connsettings[QStringLiteral("uuid")] = QVariant(s_uuid); |
847 | connsettings[QStringLiteral("type")] = QVariant(QStringLiteral("802-11-wireless")); |
848 | connection["connection"] = connsettings; |
849 | |
850 | @@ -66,6 +188,10 @@ |
851 | ipv4[QStringLiteral("routes")] = QVariant(QStringList()); |
852 | connection["ipv4"] = ipv4; |
853 | |
854 | + QVariantMap ipv6; |
855 | + ipv6[QStringLiteral("method")] = QVariant(QStringLiteral("ignore")); |
856 | + connection["ipv6"] = ipv6; |
857 | + |
858 | QVariantMap security; |
859 | security[QStringLiteral("proto")] = QVariant(QStringList{"rsn"}); |
860 | security[QStringLiteral("pairwise")] = QVariant(QStringList{"ccmp"}); |
861 | @@ -74,176 +200,510 @@ |
862 | security[QStringLiteral("psk")] = QVariant(password); |
863 | connection["802-11-wireless-security"] = security; |
864 | |
865 | - OrgFreedesktopNetworkManagerInterface mgr(nm_service, |
866 | - nm_dbus_path, |
867 | - QDBusConnection::systemBus()); |
868 | - auto reply = mgr.AddAndActivateConnection(connection, devicePath, specific); |
869 | - reply.waitForFinished(); |
870 | - if(!reply.isValid()) { |
871 | - qWarning() << "Failed to start adhoc network: " << reply.error().message() << "\n"; |
872 | - } |
873 | -} |
874 | - |
875 | -bool detectAdhoc(QString &dbusPath, QByteArray &ssid, QString &password, bool &isActive) { |
876 | - static const QString activeIface("org.freedesktop.NetworkManager.Connection.Active"); |
877 | - static const QString connProp("Connection"); |
878 | - OrgFreedesktopNetworkManagerInterface mgr(nm_service, |
879 | - nm_dbus_path, |
880 | - QDBusConnection::systemBus()); |
881 | - auto activeConnections = mgr.activeConnections(); |
882 | - OrgFreedesktopNetworkManagerSettingsInterface settings(nm_service, nm_settings_object, |
883 | - QDBusConnection::systemBus()); |
884 | - QSet<QDBusObjectPath> actives; |
885 | - auto r = settings.ListConnections(); |
886 | - r.waitForFinished(); |
887 | - for(const auto &conn : activeConnections) { |
888 | - QDBusInterface iface(nm_service, conn.path(), "org.freedesktop.DBus.Properties", |
889 | - QDBusConnection::systemBus()); |
890 | - QDBusReply<QVariant> conname = iface.call("Get", activeIface, connProp); |
891 | - if(!conname.isValid()) { |
892 | - qWarning() << "Error getting connamd: " << conname.error().message() << "\n"; |
893 | + return connection; |
894 | +} |
895 | + |
896 | +// Helper that returns a QMap<QString, QVariantMap> given a QDBusObjectPath. |
897 | +// See https://developer.gnome.org/NetworkManager/0.9/spec.html |
898 | +// #org.freedesktop.NetworkManager.Settings.Connection.GetSettings |
899 | +nmConnectionArg getConnectionSettings (QDBusObjectPath connection) { |
900 | + OrgFreedesktopNetworkManagerSettingsConnectionInterface conn( |
901 | + nm_service, connection.path(), QDBusConnection::systemBus()); |
902 | + |
903 | + auto connection_settings = conn.GetSettings(); |
904 | + connection_settings.waitForFinished(); |
905 | + return connection_settings.value(); |
906 | +} |
907 | + |
908 | + |
909 | +// Helper that returns a QMap<QString, QVariantMap> given a QDBusObjectPath. |
910 | +// See https://developer.gnome.org/NetworkManager/0.9/spec.html |
911 | +// #org.freedesktop.NetworkManager.Settings.Connection.GetSettings |
912 | +nmConnectionArg getConnectionSecrets (QDBusObjectPath connection, |
913 | + const QString key) { |
914 | + OrgFreedesktopNetworkManagerSettingsConnectionInterface conn( |
915 | + nm_service, connection.path(), QDBusConnection::systemBus()); |
916 | + auto connection_secrets = conn.GetSecrets(key); |
917 | + connection_secrets.waitForFinished(); |
918 | + return connection_secrets.value(); |
919 | +} |
920 | + |
921 | +// Helper that adds a connection and returns the QDBusObjectPath |
922 | +// of the newly created connection. |
923 | +// See https://developer.gnome.org/NetworkManager/0.9/spec.html |
924 | +// #org.freedesktop.NetworkManager.Settings.AddConnection |
925 | +QDBusObjectPath addConnection( |
926 | + const QByteArray &ssid, const QString &password, |
927 | + const QDBusObjectPath &devicePath, QString mode) { |
928 | + QDBusObjectPath invalid(""); |
929 | + |
930 | + nmConnectionArg connection = createConnectionSettings(ssid, password, |
931 | + devicePath, mode); |
932 | + |
933 | + auto add_connection_reply = nm_settings.AddConnection(connection); |
934 | + add_connection_reply.waitForFinished(); |
935 | + |
936 | + if(!add_connection_reply.isValid()) { |
937 | + qCritical() << "Failed to add connection: " |
938 | + << add_connection_reply.error().message(); |
939 | + return invalid; |
940 | + } |
941 | + return add_connection_reply.argumentAt<0>(); |
942 | +} |
943 | + |
944 | +// Returns a QDBusObjectPath of a hotspot given a mode. |
945 | +// Valid modes are 'p2p', 'ap' and 'adhoc'. |
946 | +QDBusObjectPath getHotspot(QString mode) { |
947 | + const char wifi_key[] = "802-11-wireless"; |
948 | + |
949 | + auto listed_connections = nm_settings.ListConnections(); |
950 | + listed_connections.waitForFinished(); |
951 | + |
952 | + for(const auto &connection : listed_connections.value()) { |
953 | + OrgFreedesktopNetworkManagerSettingsConnectionInterface conn( |
954 | + nm_service, connection.path(), QDBusConnection::systemBus()); |
955 | + |
956 | + auto connection_settings = getConnectionSettings(connection); |
957 | + |
958 | + if(connection_settings.find(wifi_key) != connection_settings.end()) { |
959 | + auto wifi_setup = connection_settings[wifi_key]; |
960 | + QString wifi_mode = wifi_setup["mode"].toString(); |
961 | + |
962 | + if(wifi_mode == mode) { |
963 | + return connection; |
964 | + } |
965 | + } |
966 | + } |
967 | + return QDBusObjectPath(); |
968 | +} |
969 | + |
970 | +// Returns a QDBusObjectPath of a wireless device. For now |
971 | +// it returns the first device. |
972 | +QDBusObjectPath getWirelessDevice () { |
973 | + // find the first wlan adapter for now |
974 | + auto reply1 = nm_manager.GetDevices(); |
975 | + reply1.waitForFinished(); |
976 | + |
977 | + if(!reply1.isValid()) { |
978 | + qCritical() << "Could not get network device: " |
979 | + << reply1.error().message(); |
980 | + return QDBusObjectPath(); |
981 | + } |
982 | + auto devices = reply1.value(); |
983 | + |
984 | + QDBusObjectPath dev; |
985 | + for (const auto &d : devices) { |
986 | + QDBusInterface iface(nm_service, d.path(), nm_device_interface, |
987 | + QDBusConnection::systemBus()); |
988 | + auto type_v = iface.property("DeviceType"); |
989 | + |
990 | + if (type_v.toUInt() == 2 /* NM_DEVICE_TYPE_WIFI */) { |
991 | + return d; |
992 | + } |
993 | + } |
994 | + qCritical() << "Wireless device not found, hotspot functionality is inoperative."; |
995 | + return dev; |
996 | +} |
997 | + |
998 | +// Helper to check if the hotspot on a given QDBusObjectPath is active |
999 | +// or not. It checks if the Connection.Active [1] for the given |
1000 | +// path is in NetworkManager's ActiveConnections property [2]. |
1001 | +// [1] https://developer.gnome.org/NetworkManager/0.9/spec.html |
1002 | +// #org.freedesktop.NetworkManager.Connection.Active |
1003 | +// [2] https://developer.gnome.org/NetworkManager/0.9/spec.html |
1004 | +// #org.freedesktop.NetworkManager |
1005 | +bool isHotspotActive (QDBusObjectPath path) { |
1006 | + QSet<QDBusObjectPath> active_relevant_connections; |
1007 | + auto active_connections = nm_manager.activeConnections(); |
1008 | + for(const auto &active_connection : active_connections) { |
1009 | + |
1010 | + // Active connection interface proxy. It might have a connection |
1011 | + // property we can use to deduce if this active connection represents |
1012 | + // our active hotspot. |
1013 | + QDBusInterface active_connection_dbus_interface( |
1014 | + nm_service, active_connection.path(), dbus_properties_interface, |
1015 | + QDBusConnection::systemBus()); |
1016 | + |
1017 | + // Get the Connection property, if any. |
1018 | + QDBusReply<QVariant> connection_property = |
1019 | + active_connection_dbus_interface.call("Get", |
1020 | + nm_connection_active_interface, "Connection"); |
1021 | + |
1022 | + // The Connection property did not exist, so ignore this |
1023 | + // active connection. |
1024 | + if(!connection_property.isValid()) { |
1025 | continue; |
1026 | } |
1027 | - QDBusObjectPath mainConnection = qvariant_cast<QDBusObjectPath>(conname.value()); |
1028 | - actives.insert(mainConnection); |
1029 | - } |
1030 | - const char wifiKey[] = "802-11-wireless"; |
1031 | - for(const auto &i : r.value()) { |
1032 | - OrgFreedesktopNetworkManagerSettingsConnectionInterface conn(nm_service, |
1033 | - i.path(), |
1034 | - QDBusConnection::systemBus()); |
1035 | - auto reply = conn.GetSettings(); |
1036 | - reply.waitForFinished(); |
1037 | - auto s = reply.value(); |
1038 | - if(s.find(wifiKey) != s.end()) { |
1039 | - auto wsetup = s[wifiKey]; |
1040 | - if(wsetup["mode"] == "adhoc") { |
1041 | - dbusPath = i.path(); |
1042 | - ssid = wsetup["ssid"].toByteArray(); |
1043 | - auto pwdReply = conn.GetSecrets("802-11-wireless-security"); |
1044 | - pwdReply.waitForFinished(); |
1045 | - password = pwdReply.value()["802-11-wireless-security"]["psk"].toString(); |
1046 | - isActive = false; |
1047 | - for(const auto &ac : actives) { |
1048 | - if(i == ac) { |
1049 | - isActive = true; |
1050 | - break; |
1051 | - } |
1052 | - } |
1053 | - return true; |
1054 | - } |
1055 | + |
1056 | + // It does exist, cast it to a object path. |
1057 | + QDBusObjectPath connection_path = qvariant_cast<QDBusObjectPath>( |
1058 | + connection_property.value()); |
1059 | + |
1060 | + // The object path is the same as the given hotspot path. |
1061 | + if (path == connection_path) { |
1062 | + return true; |
1063 | } |
1064 | } |
1065 | + |
1066 | + // No active connection had a Connection property equal to the |
1067 | + // given path, so return false. |
1068 | return false; |
1069 | } |
1070 | |
1071 | -QDBusObjectPath detectWirelessDevice() { |
1072 | - OrgFreedesktopNetworkManagerInterface mgr(nm_service, |
1073 | - nm_dbus_path, |
1074 | - QDBusConnection::systemBus()); |
1075 | - auto devices = mgr.GetDevices(); |
1076 | - devices.waitForFinished(); |
1077 | - for(const auto &dpath : devices.value()) { |
1078 | - QDBusInterface iface(nm_service, dpath.path(), "org.freedesktop.DBus.Properties", |
1079 | - QDBusConnection::systemBus()); |
1080 | - QDBusReply<QVariant> typeReply = iface.call("Get", "org.freedesktop.NetworkManager.Device", "DeviceType"); |
1081 | - auto typeInt = qvariant_cast<int>(typeReply.value()); |
1082 | - if(typeInt == 2) { |
1083 | - return dpath; // Assumptions are that there is only one wifi device and it is not hotpluggable. |
1084 | - } |
1085 | - } |
1086 | - qWarning() << "Wireless device not found, hotspot functionality is inoperative.\n"; |
1087 | - return QDBusObjectPath(); |
1088 | -} |
1089 | - |
1090 | std::string generate_password() { |
1091 | static const std::string items("abcdefghijklmnopqrstuvwxyz01234567890"); |
1092 | - const int passwordLength = 8; |
1093 | + const int password_length = 8; |
1094 | std::string result; |
1095 | - for(int i=0; i<passwordLength; i++) { |
1096 | + |
1097 | + for(int i=0; i<password_length; i++) { |
1098 | result.push_back(items[std::rand() % items.length()]); |
1099 | } |
1100 | return result; |
1101 | } |
1102 | - |
1103 | -} |
1104 | - |
1105 | -HotspotManager::HotspotManager(QObject *parent) : QObject(parent), |
1106 | - m_devicePath(detectWirelessDevice()) { |
1107 | +} // NetworkManager interaction |
1108 | + |
1109 | +HotspotManager::HotspotManager(QObject *parent) : |
1110 | + QObject(parent), m_mode("ap"), m_enabled(false), m_stored(false), |
1111 | + m_password(generate_password().c_str()), m_ssid(QByteArray("Ubuntu")), |
1112 | + m_device_path(getWirelessDevice()) { |
1113 | static bool isRegistered = false; |
1114 | + |
1115 | if(!isRegistered) { |
1116 | qDBusRegisterMetaType<nmConnectionArg>(); |
1117 | isRegistered = true; |
1118 | } |
1119 | - if(!detectAdhoc(m_settingsPath, m_ssid, m_password, m_isActive)) { |
1120 | - m_settingsPath = ""; |
1121 | - m_ssid = "Ubuntu hotspot"; |
1122 | - m_password = generate_password().c_str(); |
1123 | - m_isActive = false; |
1124 | - } |
1125 | -} |
1126 | - |
1127 | -QByteArray HotspotManager::getHotspotName() { |
1128 | + |
1129 | + // Stored is false if hotspot path is empty. |
1130 | + m_hotspot_path = getHotspot(m_mode); |
1131 | + setStored(!m_hotspot_path.path().isEmpty()); |
1132 | + |
1133 | + if (m_stored) { |
1134 | + updateSettingsFromDbus(m_hotspot_path); |
1135 | + } |
1136 | + |
1137 | + // Watches for new connections added to NetworkManager's Settings |
1138 | + // interface. |
1139 | + nm_settings.connection().connect( |
1140 | + nm_settings.service(), nm_settings_object, nm_settings_interface, |
1141 | + "NewConnection", this, SLOT(onNewConnection(QDBusObjectPath))); |
1142 | + |
1143 | + // Watches changes in NetworkManager. |
1144 | + nm_manager.connection().connect( |
1145 | + nm_manager.service(), nm_object, nm_service, "PropertiesChanged", this, |
1146 | + SLOT(onPropertiesChanged(QMap<QString, QVariant>))); |
1147 | +} |
1148 | + |
1149 | +void HotspotManager::setEnabled(bool value) { |
1150 | + bool blocked = setWifiBlock(true); |
1151 | + |
1152 | + // Failed to soft block, here we revert the enabled setting. |
1153 | + if (!blocked) { |
1154 | + // "The device could not be readied for configuration" |
1155 | + Q_EMIT reportError(5); |
1156 | + setEnable(false); |
1157 | + return; |
1158 | + } |
1159 | + |
1160 | + // We are enabling a hotspot |
1161 | + if (value) { |
1162 | + // If the SSID is empty, we report an error. The UI should strive |
1163 | + // to prevent us from reaching this part of the code. |
1164 | + if (m_ssid.isEmpty()) { |
1165 | + Q_EMIT reportError(1); |
1166 | + setEnable(false); |
1167 | + return; |
1168 | + } |
1169 | + |
1170 | + bool changed = changeInterfaceFirmware("/", m_mode); |
1171 | + if (!changed) { |
1172 | + // Necessary firmware for the device may be missing |
1173 | + Q_EMIT reportError(35); |
1174 | + setEnable(false); |
1175 | + setWifiBlock(false); |
1176 | + return; |
1177 | + } |
1178 | + |
1179 | + if (m_stored) { |
1180 | + // we defer enabling until old hotspot is deleted |
1181 | + // if we can delete the old one |
1182 | + // If not, unset stored flag and call this method. |
1183 | + if (!destroy(m_hotspot_path)) { |
1184 | + setStored(false); |
1185 | + setEnabled(true); |
1186 | + setEnable(true); |
1187 | + } |
1188 | + } else { |
1189 | + // we defer enabling until new hotspot is created |
1190 | + m_hotspot_path = addConnection(m_ssid, m_password, m_device_path, m_mode); |
1191 | + if (m_hotspot_path.path().isEmpty()) { |
1192 | + // Emit "Unknown Error". |
1193 | + Q_EMIT reportError(0); |
1194 | + setEnable(false); |
1195 | + setWifiBlock(false); |
1196 | + } |
1197 | + } |
1198 | + |
1199 | + } else { |
1200 | + // Disabling the hotspot. |
1201 | + disable(); |
1202 | + setEnable(false); |
1203 | + |
1204 | + bool unblocked = setWifiBlock(false); |
1205 | + if (!unblocked) { |
1206 | + // "The device could not be readied for configuration" |
1207 | + Q_EMIT reportError(5); |
1208 | + } |
1209 | + } |
1210 | +} |
1211 | + |
1212 | +// Disables a hotspot. |
1213 | +void HotspotManager::disable() { |
1214 | + QDBusObjectPath hotspot = getHotspot(m_mode); |
1215 | + if (hotspot.path().isEmpty()) { |
1216 | + qWarning() << "Could not find a hotspot setup to disable.\n"; |
1217 | + return; |
1218 | + } |
1219 | + |
1220 | + // Create Connection.Settings proxy for hotspot |
1221 | + OrgFreedesktopNetworkManagerSettingsConnectionInterface conn( |
1222 | + nm_service, hotspot.path(), QDBusConnection::systemBus()); |
1223 | + |
1224 | + // Get new settings |
1225 | + nmConnectionArg new_settings = createConnectionSettings(m_ssid, m_password, |
1226 | + m_device_path, m_mode, false); |
1227 | + auto updating = conn.Update(new_settings); |
1228 | + updating.waitForFinished(); |
1229 | + if(!updating.isValid()) { |
1230 | + qCritical() << "Could not update connection with autoconnect=false: " |
1231 | + << updating.error().message(); |
1232 | + } |
1233 | + |
1234 | + auto active_connections = nm_manager.activeConnections(); |
1235 | + for(const auto &active_connection : active_connections) { |
1236 | + // DBus Properties interface proxy of the active connection. We use |
1237 | + // this proxy to get to the Connection property. |
1238 | + QDBusInterface iface( |
1239 | + nm_service, active_connection.path(), |
1240 | + "org.freedesktop.DBus.Properties", QDBusConnection::systemBus()); |
1241 | + |
1242 | + // Call to get the Connection property. |
1243 | + QDBusReply<QVariant> conname = iface.call( |
1244 | + "Get", nm_connection_active_interface, "Connection"); |
1245 | + |
1246 | + // Cast the property to a object path. |
1247 | + QDBusObjectPath backingConnection = qvariant_cast<QDBusObjectPath>( |
1248 | + conname.value()); |
1249 | + |
1250 | + // This active connection's Connection property was our hotspot, |
1251 | + // so we will deactivate it. Note that we do not remove the hotspot, |
1252 | + // as we are storing the ssid, password and mode on the connection. |
1253 | + if(backingConnection == m_hotspot_path) { |
1254 | + // Deactivate the connection. |
1255 | + auto deactivation = nm_manager.DeactivateConnection(active_connection); |
1256 | + deactivation.waitForFinished(); |
1257 | + if(!deactivation.isValid()) { |
1258 | + qCritical() << "Could not get deactivate connection: " |
1259 | + << deactivation.error().message(); |
1260 | + } |
1261 | + return; |
1262 | + } |
1263 | + } |
1264 | + return; |
1265 | +} |
1266 | + |
1267 | +bool HotspotManager::enabled() const { |
1268 | + return m_enabled; |
1269 | +} |
1270 | + |
1271 | +void HotspotManager::setEnable(bool value) { |
1272 | + if (m_enabled != value) { |
1273 | + m_enabled = value; |
1274 | + Q_EMIT enabledChanged(value); |
1275 | + } |
1276 | +} |
1277 | + |
1278 | +bool HotspotManager::stored() const { |
1279 | + return m_stored; |
1280 | +} |
1281 | + |
1282 | +void HotspotManager::setStored(bool value) { |
1283 | + if (m_stored != value) { |
1284 | + m_stored = value; |
1285 | + Q_EMIT storedChanged(value); |
1286 | + } |
1287 | +} |
1288 | + |
1289 | +QByteArray HotspotManager::ssid() const { |
1290 | return m_ssid; |
1291 | } |
1292 | |
1293 | -QString HotspotManager::getHotspotPassword() { |
1294 | +void HotspotManager::setSsid(QByteArray value) { |
1295 | + if (m_ssid != value) { |
1296 | + m_ssid = value; |
1297 | + Q_EMIT ssidChanged(value); |
1298 | + } |
1299 | +} |
1300 | + |
1301 | +QString HotspotManager::password() const { |
1302 | return m_password; |
1303 | } |
1304 | |
1305 | -void HotspotManager::setupHotspot(QByteArray ssid_, QString password_) { |
1306 | - m_ssid = ssid_; |
1307 | - m_password = password_; |
1308 | -} |
1309 | - |
1310 | -void HotspotManager::enableHotspot() { |
1311 | - if(!m_settingsPath.isEmpty()) { |
1312 | - // Prints a warning message if the connection has disappeared already. |
1313 | - destroyHotspot(); |
1314 | - // NM returns from the dbus call immediately but only destroys the |
1315 | - // connection some time later. There is no callback for when this happens. |
1316 | - // So this is the best we can do with reasonable effort. |
1317 | - QThread::sleep(1); |
1318 | - } |
1319 | - startAdhoc(m_ssid, m_password, m_devicePath); |
1320 | - detectAdhoc(m_settingsPath, m_ssid, m_password, m_isActive); |
1321 | -} |
1322 | - |
1323 | -bool HotspotManager::isHotspotActive() { |
1324 | - return m_isActive; |
1325 | -} |
1326 | - |
1327 | -void HotspotManager::disableHotspot() { |
1328 | - static const QString activeIface("org.freedesktop.NetworkManager.Connection.Active"); |
1329 | - static const QString connProp("Connection"); |
1330 | - OrgFreedesktopNetworkManagerInterface mgr(nm_service, |
1331 | - nm_dbus_path, |
1332 | - QDBusConnection::systemBus()); |
1333 | - auto activeConnections = mgr.activeConnections(); |
1334 | - for(const auto &aConn : activeConnections) { |
1335 | - QDBusInterface iface(nm_service, aConn.path(), "org.freedesktop.DBus.Properties", |
1336 | - QDBusConnection::systemBus()); |
1337 | - QDBusReply<QVariant> conname = iface.call("Get", activeIface, connProp); |
1338 | - QDBusObjectPath backingConnection = qvariant_cast<QDBusObjectPath>(conname.value()); |
1339 | - if(backingConnection.path() == m_settingsPath) { |
1340 | - mgr.DeactivateConnection(aConn); |
1341 | - return; |
1342 | +void HotspotManager::setPassword(QString value) { |
1343 | + if (m_password != value) { |
1344 | + m_password = value; |
1345 | + Q_EMIT passwordChanged(value); |
1346 | + } |
1347 | +} |
1348 | + |
1349 | +QString HotspotManager::mode() const { |
1350 | + return m_mode; |
1351 | +} |
1352 | + |
1353 | +void HotspotManager::setMode(QString value) { |
1354 | + if (m_mode != value) { |
1355 | + m_mode = value; |
1356 | + Q_EMIT modeChanged(value); |
1357 | + } |
1358 | +} |
1359 | + |
1360 | +void HotspotManager::onNewConnection(QDBusObjectPath path) { |
1361 | + // The new connection is the same as the stored hotspot path. |
1362 | + if (path == m_hotspot_path) { |
1363 | + // If a new hotspot was added, it is also given that |
1364 | + // Wi-Fi has been soft blocked. We can now unblock Wi-Fi |
1365 | + // and let NetworkManager pick up the newly created |
1366 | + // connection. |
1367 | + bool unblocked = setWifiBlock(false); |
1368 | + if (!unblocked) { |
1369 | + // "The device could not be readied for configuration" |
1370 | + Q_EMIT reportError(5); |
1371 | + } else { |
1372 | + // We successfully unblocked the Wi-Fi, so set m_enable to true. |
1373 | + setEnable(true); |
1374 | } |
1375 | - } |
1376 | - qWarning() << "Could not find a hotspot setup to disable.\n"; |
1377 | -} |
1378 | - |
1379 | -void HotspotManager::destroyHotspot() { |
1380 | - if(m_settingsPath.isEmpty()) { |
1381 | - qWarning() << "Tried to destroy nonexisting hotspot.\n"; |
1382 | + |
1383 | + // This also mean we have successfully created a hotspot connection |
1384 | + // object in NetworkManager, so m_stored should now be true. |
1385 | + setStored(true); |
1386 | + } |
1387 | +} |
1388 | + |
1389 | + |
1390 | +bool HotspotManager::destroy(QDBusObjectPath path) { |
1391 | + if(path.path().isEmpty()) { |
1392 | + return false; |
1393 | + } |
1394 | + |
1395 | + // Connection Settings interface proxy for the connection |
1396 | + // we are about to delete. |
1397 | + OrgFreedesktopNetworkManagerSettingsConnectionInterface conn( |
1398 | + nm_service, path.path(), QDBusConnection::systemBus()); |
1399 | + |
1400 | + // Subscribe to the connection proxy's Removed signal. |
1401 | + conn.connection().connect(conn.service(), path.path(), conn.interface(), |
1402 | + "Removed", this, SLOT(onRemoved())); |
1403 | + |
1404 | + auto del = conn.Delete(); |
1405 | + del.waitForFinished(); |
1406 | + return del.isValid(); |
1407 | +} |
1408 | + |
1409 | +void HotspotManager::onRemoved() { |
1410 | + // The UI does not support direct deletion of a hotspot, and given how |
1411 | + // hotspots currently work, every time we want to re-use a hotspot, we |
1412 | + // delete it an add a new one. |
1413 | + // Thus, if a hotspot was deleted, we now create a new one. |
1414 | + m_hotspot_path = addConnection(m_ssid, m_password, m_device_path, m_mode); |
1415 | + |
1416 | + // We could not add a connection, so report, disable and unblock. |
1417 | + if (m_hotspot_path.path().isEmpty()) { |
1418 | + // Emit "Unknown Error". |
1419 | + Q_EMIT reportError(0); |
1420 | + setEnable(false); |
1421 | + setWifiBlock(false); |
1422 | + } |
1423 | +} |
1424 | + |
1425 | +void HotspotManager::onPropertiesChanged(QVariantMap properties) { |
1426 | + // If we have no hotspot path, ignore changes in NetworkManager. |
1427 | + if (m_hotspot_path.path().isEmpty()) { |
1428 | return; |
1429 | } |
1430 | - QDBusInterface control(nm_service, m_settingsPath, nm_connection_interface, |
1431 | - QDBusConnection::systemBus()); |
1432 | - QDBusReply<void> reply = control.call("Delete"); |
1433 | - if(!reply.isValid()) { |
1434 | - qWarning() << "Could not disconnect adhoc network: " << reply.error().message() << "\n"; |
1435 | - } else { |
1436 | - m_isActive = false; |
1437 | + |
1438 | + // Set flag so we know that ActiveConnections changed. |
1439 | + bool active_connection_changed = false; |
1440 | + |
1441 | + for(QVariantMap::const_iterator iter = properties.begin(); iter != properties.end(); ++iter) { |
1442 | + if (iter.key() == "ActiveConnections") { |
1443 | + active_connection_changed = true; |
1444 | + |
1445 | + const QDBusArgument args = qvariant_cast<QDBusArgument>(iter.value()); |
1446 | + if (args.currentType() == QDBusArgument::ArrayType) { |
1447 | + args.beginArray(); |
1448 | + |
1449 | + while (!args.atEnd()) { |
1450 | + QDBusObjectPath path = qdbus_cast<QDBusObjectPath>(args); |
1451 | + |
1452 | + QDBusInterface active_connection_dbus_interface( |
1453 | + nm_service, path.path(), dbus_properties_interface, |
1454 | + QDBusConnection::systemBus()); |
1455 | + |
1456 | + QDBusReply<QVariant> connection_property = active_connection_dbus_interface.call( |
1457 | + "Get", nm_connection_active_interface, "Connection"); |
1458 | + |
1459 | + // Active connection did not have a connection property, |
1460 | + // so ignore it. |
1461 | + if(!connection_property.isValid()) { |
1462 | + continue; |
1463 | + } |
1464 | + |
1465 | + QDBusObjectPath connection_path = qvariant_cast<QDBusObjectPath>( |
1466 | + connection_property.value()); |
1467 | + |
1468 | + // We see our connection as being active, so we emit that is |
1469 | + // enabled and return. |
1470 | + if (connection_path == m_hotspot_path) { |
1471 | + setEnable(true); |
1472 | + return; |
1473 | + } |
1474 | + } |
1475 | + args.endArray(); |
1476 | + } |
1477 | + } |
1478 | + } |
1479 | + |
1480 | + // At this point ActiveConnections changed, but |
1481 | + // our hotspot was not in that list. |
1482 | + if (active_connection_changed) { |
1483 | + setEnable(false); |
1484 | + } |
1485 | +} |
1486 | + |
1487 | +void HotspotManager::updateSettingsFromDbus(QDBusObjectPath path) { |
1488 | + setEnable(isHotspotActive(m_hotspot_path)); |
1489 | + |
1490 | + nmConnectionArg settings = getConnectionSettings(path); |
1491 | + const char wifi_key[] = "802-11-wireless"; |
1492 | + const char security_key[] = "802-11-wireless-security"; |
1493 | + |
1494 | + if (settings.find(wifi_key) != settings.end()) { |
1495 | + QByteArray ssid = settings[wifi_key]["ssid"].toByteArray(); |
1496 | + if (!ssid.isEmpty()) { |
1497 | + setSsid(ssid); |
1498 | + } |
1499 | + |
1500 | + QString mode = settings[wifi_key]["mode"].toString(); |
1501 | + if (!mode.isEmpty()) { |
1502 | + setMode(mode); |
1503 | + } |
1504 | + } |
1505 | + |
1506 | + nmConnectionArg secrets = getConnectionSecrets(path, security_key); |
1507 | + |
1508 | + if (secrets.find(security_key) != secrets.end()) { |
1509 | + QString pwd = secrets[security_key]["psk"].toString(); |
1510 | + if (!pwd.isEmpty()) { |
1511 | + setPassword(pwd); |
1512 | + } |
1513 | } |
1514 | } |
1515 | |
1516 | === modified file 'plugins/cellular/hotspotmanager.h' |
1517 | --- plugins/cellular/hotspotmanager.h 2014-07-23 13:44:31 +0000 |
1518 | +++ plugins/cellular/hotspotmanager.h 2015-05-18 15:06:15 +0000 |
1519 | @@ -1,8 +1,9 @@ |
1520 | /* |
1521 | - * Copyright (C) 2014 Canonical, Ltd. |
1522 | + * Copyright (C) 2014, 2015 Canonical, Ltd. |
1523 | * |
1524 | * Authors: |
1525 | * Jussi Pakkanen <jussi.pakkanen@canonical.com> |
1526 | + * Jonas G. Drange <jonas.drange@canonical.com> |
1527 | * |
1528 | * This program is free software: you can redistribute it and/or modify it |
1529 | * under the terms of the GNU General Public License version 3, as published |
1530 | @@ -15,39 +16,166 @@ |
1531 | * |
1532 | * You should have received a copy of the GNU General Public License |
1533 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1534 | - */ |
1535 | + * |
1536 | + * |
1537 | + * HotspotManager API |
1538 | + * ============================== |
1539 | + * |
1540 | + * Methods |
1541 | + * |
1542 | + * Signals |
1543 | + * enabledChanged(bool enabled) |
1544 | + * Signal that gets emitted when the hotspot is disabled or enabled. |
1545 | + * |
1546 | + * storedChanged(bool stored) |
1547 | + * Signal that gets emitted when a hotspot was stored. |
1548 | + * |
1549 | + * ssidChanged(QByteArray ssid) |
1550 | + * Signal that gets emitted when the ssid of the hotspot was changed. |
1551 | + * |
1552 | + * passwordChanged(QString password) |
1553 | + * Signal that gets emitted when the password of the hotspot was changed. |
1554 | + * |
1555 | + * modeChanged(QString mode) |
1556 | + * Signal that gets emitted when the mode was changed. |
1557 | + * |
1558 | + * authChanged(QString auth) |
1559 | + * |
1560 | + * Note that none of these signal will be emitted if a change to the hotspot |
1561 | + * was made by anyone else than the HotspotManager. Right now, the canonical |
1562 | + * way to edit a hotspot, is through the hotspot manager. |
1563 | + * |
1564 | + * |
1565 | + * reportError(int reason) |
1566 | + * The reasons correspond to https://developer.gnome.org/ |
1567 | + * NetworkManager/0.9/spec.html#type-NM_DEVICE_STATE_REASON |
1568 | + * |
1569 | + * Properties |
1570 | + * bool enabled [readwrite] |
1571 | + * Whether or not the hotspot is enabled. |
1572 | + * |
1573 | + * bool stored [readonly] |
1574 | + * Whether or not a hotspot is known to the hotspotmanager. |
1575 | + * |
1576 | + * QByteArray ssid [readwrite] |
1577 | + * The current SSID of the hotspot. |
1578 | + * |
1579 | + * QString auth [readwrite] |
1580 | + * The current authentication of the hotspot. The default for this property |
1581 | + * is "wpa-psk" and is currently the only supported scheme. WEP is unsupported |
1582 | + * by design, as is no scheme at all. |
1583 | + * |
1584 | + * TODO: Check/add support for wpa-eap |
1585 | + * |
1586 | + * QString password [readwrite] |
1587 | + * The current Pre-Shared-Key for the hotspot. If the key is 64-characters |
1588 | + * long, it must contain only hexadecimal characters and is interpreted as a |
1589 | + * hexadecimal WPA key. Otherwise, the key must be between 8 and 63 ASCII |
1590 | + * characters and is interpreted as a WPA passphrase. |
1591 | + * |
1592 | + * QString mode [readwrite, optional] |
1593 | + * The current hotspot mode. The default of this value is "ap", but can be |
1594 | + * set to "p2p" or "adhoc". "p2p" and "adhoc" is currently not fully supported. |
1595 | + * |
1596 | + * TODO: Complete support for adhoc and p2p modes. |
1597 | +*/ |
1598 | |
1599 | -#ifndef CELLULAR_DBUS_HELPER |
1600 | -#define CELLULAR_DBUS_HELPER |
1601 | +#ifndef HOTSPOTMANAGER_H |
1602 | +#define HOTSPOTMANAGER_H |
1603 | |
1604 | #include <QObject> |
1605 | #include <QDBusObjectPath> |
1606 | -/** |
1607 | - * For exposing dbus data to Qml. |
1608 | - */ |
1609 | +#include <QUuid> |
1610 | + |
1611 | +#include "nm_manager_proxy.h" |
1612 | +#include "nm_settings_proxy.h" |
1613 | +#include "nm_settings_connection_proxy.h" |
1614 | |
1615 | class HotspotManager : public QObject { |
1616 | Q_OBJECT |
1617 | + Q_PROPERTY( bool enabled |
1618 | + READ enabled |
1619 | + WRITE setEnabled |
1620 | + NOTIFY enabledChanged) |
1621 | + Q_PROPERTY( QByteArray ssid |
1622 | + READ ssid |
1623 | + WRITE setSsid |
1624 | + NOTIFY ssidChanged) |
1625 | + Q_PROPERTY( QString auth |
1626 | + READ auth |
1627 | + WRITE setAuth |
1628 | + NOTIFY authChanged) |
1629 | + Q_PROPERTY( QString password |
1630 | + READ password |
1631 | + WRITE setPassword |
1632 | + NOTIFY passwordChanged) |
1633 | + Q_PROPERTY( QString mode |
1634 | + READ mode |
1635 | + WRITE setMode |
1636 | + NOTIFY modeChanged) |
1637 | + Q_PROPERTY( bool stored |
1638 | + READ stored |
1639 | + NOTIFY storedChanged) |
1640 | |
1641 | public: |
1642 | explicit HotspotManager(QObject *parent = nullptr); |
1643 | ~HotspotManager() {}; |
1644 | |
1645 | - Q_INVOKABLE QByteArray getHotspotName(); |
1646 | - Q_INVOKABLE QString getHotspotPassword(); |
1647 | - Q_INVOKABLE void setupHotspot(QByteArray ssid, QString password); |
1648 | - Q_INVOKABLE bool isHotspotActive(); |
1649 | - Q_INVOKABLE void enableHotspot(); |
1650 | - Q_INVOKABLE void disableHotspot(); |
1651 | - void destroyHotspot(); |
1652 | + bool enabled() const; |
1653 | + void setEnabled(bool); |
1654 | + bool stored() const; |
1655 | + |
1656 | + QByteArray ssid() const; |
1657 | + void setSsid(QByteArray); |
1658 | + |
1659 | + QString password() const; |
1660 | + void setPassword(QString); |
1661 | + |
1662 | + QString mode() const; |
1663 | + void setMode(QString); |
1664 | + |
1665 | + QString auth() const; |
1666 | + void setAuth(QString); |
1667 | + |
1668 | +Q_SIGNALS: |
1669 | + void enabledChanged(bool enabled); |
1670 | + void storedChanged(bool stored); |
1671 | + void ssidChanged(const QByteArray ssid); |
1672 | + void passwordChanged(const QString password); |
1673 | + void modeChanged(const QString mode); |
1674 | + void authChanged(const QString auth); |
1675 | + |
1676 | + /* |
1677 | + The mapping of code to string is taken from |
1678 | + http://bazaar.launchpad.net/~vcs-imports/ |
1679 | + network-manager/trunk/view/head:/cli/src/common.c |
1680 | + |
1681 | + NetworkManager documentation: https://developer.gnome.org/ |
1682 | + NetworkManager/0.9/spec.html#type-NM_DEVICE_STATE_REASON |
1683 | + */ |
1684 | + void reportError(const int &reason); |
1685 | + |
1686 | +public Q_SLOTS: |
1687 | + void onNewConnection(const QDBusObjectPath); |
1688 | + void onRemoved(); |
1689 | + void onPropertiesChanged(const QVariantMap); |
1690 | |
1691 | private: |
1692 | + QString m_mode; |
1693 | + bool m_enabled; |
1694 | + bool m_stored; |
1695 | + QString m_password; |
1696 | QByteArray m_ssid; |
1697 | - QString m_password; |
1698 | - QString m_settingsPath; |
1699 | - QDBusObjectPath m_devicePath; |
1700 | - bool m_isActive; |
1701 | + QDBusObjectPath m_device_path; |
1702 | + QDBusObjectPath m_hotspot_path; |
1703 | + |
1704 | + void disable(); |
1705 | + |
1706 | + bool destroy(QDBusObjectPath); |
1707 | + |
1708 | + void setStored(bool); |
1709 | + void setEnable(bool); |
1710 | + void updateSettingsFromDbus(QDBusObjectPath); |
1711 | }; |
1712 | |
1713 | - |
1714 | #endif |
1715 | |
1716 | === modified file 'plugins/system-update/system_update.cpp' |
1717 | --- plugins/system-update/system_update.cpp 2014-10-28 17:49:47 +0000 |
1718 | +++ plugins/system-update/system_update.cpp 2015-05-18 15:06:15 +0000 |
1719 | @@ -105,6 +105,7 @@ |
1720 | reply.waitForFinished(); |
1721 | if (reply.isValid()) { |
1722 | m_currentBuildNumber = reply.argumentAt<0>(); |
1723 | + m_deviceName = reply.argumentAt<2>(); |
1724 | m_lastUpdateDate = QDateTime::fromString(reply.argumentAt<3>(), Qt::ISODate); |
1725 | m_detailedVersion = reply.argumentAt<4>(); |
1726 | Q_EMIT versionChanged(); |
1727 | @@ -129,6 +130,12 @@ |
1728 | return target > current; |
1729 | } |
1730 | |
1731 | +QString SystemUpdate::deviceName() { |
1732 | + if (m_deviceName.isNull()) |
1733 | + setCurrentDetailedVersion(); |
1734 | + |
1735 | + return m_deviceName; |
1736 | +} |
1737 | |
1738 | QDateTime SystemUpdate::lastUpdateDate() { |
1739 | if (!m_lastUpdateDate.isValid()) |
1740 | |
1741 | === modified file 'plugins/system-update/system_update.h' |
1742 | --- plugins/system-update/system_update.h 2014-09-26 20:14:54 +0000 |
1743 | +++ plugins/system-update/system_update.h 2015-05-18 15:06:15 +0000 |
1744 | @@ -48,6 +48,7 @@ |
1745 | int currentBuildNumber(); |
1746 | QString currentUbuntuBuildNumber(); |
1747 | QString currentDeviceBuildNumber(); |
1748 | + QString deviceName(); |
1749 | |
1750 | void checkForUpdate(); |
1751 | void downloadUpdate(); |
1752 | @@ -80,6 +81,7 @@ |
1753 | QMap<QString, QString> m_detailedVersion; |
1754 | QDateTime m_lastUpdateDate; |
1755 | int m_downloadMode; |
1756 | + QString m_deviceName; |
1757 | |
1758 | QDBusConnection m_systemBusConnection; |
1759 | QString m_objectPath; |
1760 | |
1761 | === modified file 'plugins/system-update/update_manager.h' |
1762 | --- plugins/system-update/update_manager.h 2015-03-16 13:51:36 +0000 |
1763 | +++ plugins/system-update/update_manager.h 2015-05-18 15:06:15 +0000 |
1764 | @@ -62,6 +62,8 @@ |
1765 | NOTIFY versionChanged) |
1766 | Q_PROPERTY(QString currentDeviceBuildNumber READ currentDeviceBuildNumber |
1767 | NOTIFY versionChanged) |
1768 | + Q_PROPERTY(QString deviceName READ deviceName |
1769 | + NOTIFY deviceNameChanged) |
1770 | |
1771 | Q_SIGNALS: |
1772 | void checkFinished(); |
1773 | @@ -78,6 +80,7 @@ |
1774 | void updateProcessFailed(QString message); |
1775 | void systemUpdateFailed(int consecutiveFailureCount, QString lastReason); |
1776 | void versionChanged(); |
1777 | + void deviceNameChanged(); |
1778 | void rebooting(bool status); |
1779 | |
1780 | public: |
1781 | @@ -97,6 +100,7 @@ |
1782 | QDateTime lastUpdateDate() { return m_systemUpdate.lastUpdateDate(); } |
1783 | QString currentUbuntuBuildNumber() { return m_systemUpdate.currentUbuntuBuildNumber(); } |
1784 | QString currentDeviceBuildNumber() { return m_systemUpdate.currentDeviceBuildNumber(); } |
1785 | + QString deviceName() { return m_systemUpdate.deviceName(); } |
1786 | bool checkTarget() { return m_systemUpdate.checkTarget(); } |
1787 | |
1788 | |
1789 | |
1790 | === modified file 'tests/autopilot/ubuntu_system_settings/__init__.py' |
1791 | --- tests/autopilot/ubuntu_system_settings/__init__.py 2015-05-12 16:29:02 +0000 |
1792 | +++ tests/autopilot/ubuntu_system_settings/__init__.py 2015-05-18 15:06:15 +0000 |
1793 | @@ -306,6 +306,138 @@ |
1794 | field.write(name) |
1795 | self.pointing_device.click_object(ok) |
1796 | |
1797 | + """ |
1798 | + :returns: Whether or not hotspot can be used. |
1799 | + """ |
1800 | + @autopilot.logging.log_action(logger.debug) |
1801 | + def have_hotspot(self): |
1802 | + return self.wait_select_single(objectName='hotspotEntry').visible |
1803 | + |
1804 | + """ |
1805 | + :param: Configuration with keys ssid and password. |
1806 | + :returns: Hotspot page. |
1807 | + """ |
1808 | + @autopilot.logging.log_action(logger.debug) |
1809 | + def setup_hotspot(self, config=None): |
1810 | + hotspot_page = self._enter_hotspot() |
1811 | + hotspot_page.setup_hotspot(config) |
1812 | + return hotspot_page |
1813 | + |
1814 | + """ |
1815 | + Enables hotspot. |
1816 | + :returns: Hotspot page. |
1817 | + """ |
1818 | + @autopilot.logging.log_action(logger.debug) |
1819 | + def enable_hotspot(self): |
1820 | + hotspot_page = self._enter_hotspot() |
1821 | + hotspot_page.enable_hotspot() |
1822 | + return hotspot_page |
1823 | + |
1824 | + """ |
1825 | + Disables hotspot. |
1826 | + :returns: Hotspot page. |
1827 | + """ |
1828 | + @autopilot.logging.log_action(logger.debug) |
1829 | + def disable_hotspot(self): |
1830 | + hotspot_page = self._enter_hotspot() |
1831 | + hotspot_page.disable_hotspot() |
1832 | + return hotspot_page |
1833 | + |
1834 | + @autopilot.logging.log_action(logger.debug) |
1835 | + def _enter_hotspot(self): |
1836 | + obj = self.wait_select_single(objectName="hotspotEntry") |
1837 | + self.pointing_device.click_object(obj) |
1838 | + return self.get_root_instance().wait_select_single( |
1839 | + objectName='hotspotPage') |
1840 | + |
1841 | + |
1842 | +class Hotspot(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
1843 | + |
1844 | + """Autopilot helper for Hotspot page.""" |
1845 | + |
1846 | + @classmethod |
1847 | + def validate_dbus_object(cls, path, state): |
1848 | + name = introspection.get_classname_from_path(path) |
1849 | + if name == b'ItemPage': |
1850 | + if state['objectName'][1] == 'hotspotPage': |
1851 | + return True |
1852 | + return False |
1853 | + |
1854 | + @property |
1855 | + def _switch(self): |
1856 | + return self.wait_select_single( |
1857 | + ubuntuuitoolkit.CheckBox, |
1858 | + objectName='hotspotSwitch') |
1859 | + |
1860 | + @autopilot.logging.log_action(logger.debug) |
1861 | + def enable_hotspot(self): |
1862 | + self._switch.check() |
1863 | + |
1864 | + @autopilot.logging.log_action(logger.debug) |
1865 | + def disable_hotspot(self): |
1866 | + self._switch.uncheck() |
1867 | + |
1868 | + @autopilot.logging.log_action(logger.debug) |
1869 | + def setup_hotspot(self, config): |
1870 | + obj = self.select_single(objectName='hotspotSetupEntry') |
1871 | + self.pointing_device.click_object(obj) |
1872 | + setup = self.get_root_instance().wait_select_single( |
1873 | + objectName='hotspotSetup') |
1874 | + if config: |
1875 | + if 'ssid' in config: |
1876 | + setup.set_ssid(config['ssid']) |
1877 | + if 'password' in config: |
1878 | + setup.set_password(config['password']) |
1879 | + setup.enable() |
1880 | + if setup: |
1881 | + setup.wait_until_destroyed() |
1882 | + |
1883 | + @autopilot.logging.log_action(logger.debug) |
1884 | + def get_hotspot_status(self): |
1885 | + return self._switch.checked |
1886 | + |
1887 | + |
1888 | +class HotspotSetup(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
1889 | + |
1890 | + """Autopilot helper for Hotspot setup.""" |
1891 | + |
1892 | + @classmethod |
1893 | + def validate_dbus_object(cls, path, state): |
1894 | + name = introspection.get_classname_from_path(path) |
1895 | + if name == b'Dialog': |
1896 | + if state['objectName'][1] == 'hotspotSetup': |
1897 | + return True |
1898 | + return False |
1899 | + |
1900 | + @property |
1901 | + def _ssid_field(self): |
1902 | + return self.wait_select_single( |
1903 | + ubuntuuitoolkit.TextField, |
1904 | + objectName='ssidField') |
1905 | + |
1906 | + @property |
1907 | + def _password_field(self): |
1908 | + return self.wait_select_single( |
1909 | + ubuntuuitoolkit.TextField, |
1910 | + objectName='passwordField') |
1911 | + |
1912 | + @property |
1913 | + def _enable_button(self): |
1914 | + return self.wait_select_single( |
1915 | + 'Button', objectName='confirmButton') |
1916 | + |
1917 | + @autopilot.logging.log_action(logger.debug) |
1918 | + def set_ssid(self, ssid): |
1919 | + self._ssid_field.write(ssid) |
1920 | + |
1921 | + @autopilot.logging.log_action(logger.debug) |
1922 | + def set_password(self, password): |
1923 | + self._password_field.write(password) |
1924 | + |
1925 | + @autopilot.logging.log_action(logger.debug) |
1926 | + def enable(self): |
1927 | + self.pointing_device.click_object(self._enable_button) |
1928 | + |
1929 | |
1930 | class BluetoothPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
1931 | |
1932 | |
1933 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py' |
1934 | --- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2015-04-16 15:24:41 +0000 |
1935 | +++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2015-05-18 15:06:15 +0000 |
1936 | @@ -31,7 +31,6 @@ |
1937 | from gi.repository import UPowerGlib |
1938 | from testtools.matchers import Equals, NotEquals, GreaterThan |
1939 | |
1940 | - |
1941 | ACCOUNTS_IFACE = 'org.freedesktop.Accounts' |
1942 | ACCOUNTS_USER_IFACE = 'org.freedesktop.Accounts.User' |
1943 | ACCOUNTS_OBJ = '/org/freedesktop/Accounts' |
1944 | @@ -384,6 +383,68 @@ |
1945 | self.cellular_page = self.main_view.go_to_cellular_page() |
1946 | |
1947 | |
1948 | +class HotspotBaseTestCase(CellularBaseTestCase): |
1949 | + |
1950 | + @classmethod |
1951 | + def setUpClass(cls): |
1952 | + super(HotspotBaseTestCase, cls).setUpClass() |
1953 | + nm_tmpl = os.path.join(os.path.dirname(__file__), 'networkmanager.py') |
1954 | + (cls.n_mock, cls.obj_nm) = cls.spawn_server_template( |
1955 | + nm_tmpl, stdout=subprocess.PIPE) |
1956 | + (cls.u_mock, cls.obj_urf) = cls.spawn_server_template( |
1957 | + 'urfkill', stdout=subprocess.PIPE) |
1958 | + |
1959 | + @classmethod |
1960 | + def tearDownClass(cls): |
1961 | + cls.n_mock.terminate() |
1962 | + cls.n_mock.wait() |
1963 | + cls.u_mock.terminate() |
1964 | + cls.u_mock.wait() |
1965 | + super(HotspotBaseTestCase, cls).tearDownClass() |
1966 | + |
1967 | + def tearDown(self): |
1968 | + self.obj_nm.Reset() |
1969 | + self.urfkill_mock.ClearCalls() |
1970 | + super(HotspotBaseTestCase, self).tearDown() |
1971 | + |
1972 | + def setUp(self): |
1973 | + self.patch_environment("USS_SHOW_ALL_UI", "1") |
1974 | + self.nm_mock = dbus.Interface(self.obj_nm, dbusmock.MOCK_IFACE) |
1975 | + self.device_path = self.obj_nm.AddWiFiDevice('test0', 'Barbaz', 1) |
1976 | + self.device_mock = dbus.Interface(self.dbus_con.get_object( |
1977 | + NM_SERVICE, self.device_path), |
1978 | + 'org.freedesktop.DBus.Properties') |
1979 | + self.urfkill_mock = dbus.Interface(self.obj_urf, dbusmock.MOCK_IFACE) |
1980 | + super(HotspotBaseTestCase, self).setUp() |
1981 | + |
1982 | + def add_hotspot(self, name, password, secured=True, enabled=False): |
1983 | + settings = { |
1984 | + 'connection': { |
1985 | + 'id': dbus.String('Test AP', variant_level=1), |
1986 | + 'type': dbus.String('802-11-wireless', variant_level=1), }, |
1987 | + '802-11-wireless': { |
1988 | + 'mode': dbus.String('ap', variant_level=1), |
1989 | + 'ssid': dbus.String(name, variant_level=1), |
1990 | + } |
1991 | + } |
1992 | + |
1993 | + if secured: |
1994 | + settings['802-11-wireless']['security'] = dbus.String( |
1995 | + '802-11-wireless-security', variant_level=1) |
1996 | + settings['802-11-wireless-security'] = { |
1997 | + 'auth-alg': dbus.String('shared', variant_level=1), |
1998 | + 'key-mgmt': dbus.String('wpa-psk', variant_level=1), |
1999 | + 'psk': dbus.String(password, variant_level=1), |
2000 | + } |
2001 | + |
2002 | + if enabled: |
2003 | + settings['connection']['autoconnect'] = True |
2004 | + |
2005 | + connection_path = self.obj_nm.SettingsAddConnection(settings) |
2006 | + |
2007 | + return connection_path |
2008 | + |
2009 | + |
2010 | class BluetoothBaseTestCase(UbuntuSystemSettingsTestCase): |
2011 | |
2012 | def setUp(self): |
2013 | |
2014 | === added file 'tests/autopilot/ubuntu_system_settings/tests/networkmanager.py' |
2015 | --- tests/autopilot/ubuntu_system_settings/tests/networkmanager.py 1970-01-01 00:00:00 +0000 |
2016 | +++ tests/autopilot/ubuntu_system_settings/tests/networkmanager.py 2015-05-18 15:06:15 +0000 |
2017 | @@ -0,0 +1,902 @@ |
2018 | +'''NetworkManager mock template |
2019 | + |
2020 | +This creates the expected methods and properties of the main |
2021 | +org.freedesktop.NetworkManager object, but no devices. You can specify any |
2022 | +property such as 'NetworkingEnabled', or 'WirelessEnabled' etc. in |
2023 | +"parameters". |
2024 | +''' |
2025 | + |
2026 | +# This program is free software; you can redistribute it and/or modify it under |
2027 | +# the terms of the GNU Lesser General Public License as published by the Free |
2028 | +# Software Foundation; either version 3 of the License, or (at your option) any |
2029 | +# later version. See http://www.gnu.org/copyleft/lgpl.html for the full text |
2030 | +# of the license. |
2031 | + |
2032 | +__author__ = 'Iftikhar Ahmad' |
2033 | +__email__ = 'iftikhar.ahmad@canonical.com' |
2034 | +__copyright__ = '(c) 2012 Canonical Ltd.' |
2035 | +__license__ = 'LGPL 3+' |
2036 | + |
2037 | +import dbus |
2038 | +import uuid |
2039 | +import binascii |
2040 | + |
2041 | +from dbusmock import MOCK_IFACE |
2042 | +import dbusmock |
2043 | + |
2044 | + |
2045 | +BUS_NAME = 'org.freedesktop.NetworkManager' |
2046 | +MAIN_OBJ = '/org/freedesktop/NetworkManager' |
2047 | +MAIN_IFACE = 'org.freedesktop.NetworkManager' |
2048 | +SETTINGS_OBJ = '/org/freedesktop/NetworkManager/Settings' |
2049 | +SETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings' |
2050 | +DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device' |
2051 | +WIRELESS_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless' |
2052 | +ACCESS_POINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint' |
2053 | +CSETTINGS_IFACE = 'org.freedesktop.NetworkManager.Settings.Connection' |
2054 | +ACTIVE_CONNECTION_IFACE = 'org.freedesktop.NetworkManager.Connection.Active' |
2055 | +SYSTEM_BUS = True |
2056 | + |
2057 | + |
2058 | +class NMState: |
2059 | + '''Global state |
2060 | + |
2061 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2062 | + type-NM_STATE |
2063 | + ''' |
2064 | + NM_STATE_UNKNOWN = 0 |
2065 | + NM_STATE_ASLEEP = 10 |
2066 | + NM_STATE_DISCONNECTED = 20 |
2067 | + NM_STATE_DISCONNECTING = 30 |
2068 | + NM_STATE_CONNECTING = 40 |
2069 | + NM_STATE_CONNECTED_LOCAL = 50 |
2070 | + NM_STATE_CONNECTED_SITE = 60 |
2071 | + NM_STATE_CONNECTED_GLOBAL = 70 |
2072 | + |
2073 | + |
2074 | +class NMConnectivityState: |
2075 | + '''Connectvity state |
2076 | + |
2077 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2078 | + type-NM_CONNECTIVITY |
2079 | + ''' |
2080 | + NM_CONNECTIVITY_UNKNOWN = 0 |
2081 | + NM_CONNECTIVITY_NONE = 1 |
2082 | + NM_CONNECTIVITY_PORTAL = 2 |
2083 | + NM_CONNECTIVITY_LIMITED = 3 |
2084 | + NM_CONNECTIVITY_FULL = 4 |
2085 | + |
2086 | + |
2087 | +class NMActiveConnectionState: |
2088 | + '''Active connection state |
2089 | + |
2090 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2091 | + type-NM_ACTIVE_CONNECTION_STATE |
2092 | + ''' |
2093 | + NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0 |
2094 | + NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1 |
2095 | + NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2 |
2096 | + NM_ACTIVE_CONNECTION_STATE_DEACTIVATING = 3 |
2097 | + NM_ACTIVE_CONNECTION_STATE_DEACTIVATED = 4 |
2098 | + |
2099 | + |
2100 | +class InfrastructureMode: |
2101 | + '''Infrastructure mode |
2102 | + |
2103 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2104 | + type-NM_802_11_MODE |
2105 | + ''' |
2106 | + NM_802_11_MODE_UNKNOWN = 0 |
2107 | + NM_802_11_MODE_ADHOC = 1 |
2108 | + NM_802_11_MODE_INFRA = 2 |
2109 | + NM_802_11_MODE_AP = 3 |
2110 | + |
2111 | + NAME_MAP = { |
2112 | + NM_802_11_MODE_UNKNOWN: 'unknown', |
2113 | + NM_802_11_MODE_ADHOC: 'adhoc', |
2114 | + NM_802_11_MODE_INFRA: 'infrastructure', |
2115 | + NM_802_11_MODE_AP: 'access-point', |
2116 | + } |
2117 | + |
2118 | + |
2119 | +class DeviceState: |
2120 | + '''Device states |
2121 | + |
2122 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2123 | + type-NM_DEVICE_STATE |
2124 | + ''' |
2125 | + UNKNOWN = 0 |
2126 | + UNMANAGED = 10 |
2127 | + UNAVAILABLE = 20 |
2128 | + DISCONNECTED = 30 |
2129 | + PREPARE = 40 |
2130 | + CONFIG = 50 |
2131 | + NEED_AUTH = 60 |
2132 | + IP_CONFIG = 70 |
2133 | + IP_CHECK = 80 |
2134 | + SECONDARIES = 90 |
2135 | + ACTIVATED = 100 |
2136 | + DEACTIVATING = 110 |
2137 | + FAILED = 120 |
2138 | + |
2139 | + |
2140 | +class NM80211ApSecurityFlags: |
2141 | + '''Security flags |
2142 | + |
2143 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2144 | + type-NM_802_11_AP_SEC |
2145 | + ''' |
2146 | + NM_802_11_AP_SEC_NONE = 0x00000000 |
2147 | + NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001 |
2148 | + NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002 |
2149 | + NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004 |
2150 | + NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008 |
2151 | + NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010 |
2152 | + NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020 |
2153 | + NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040 |
2154 | + NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080 |
2155 | + NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100 |
2156 | + NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200 |
2157 | + |
2158 | + NAME_MAP = { |
2159 | + NM_802_11_AP_SEC_KEY_MGMT_PSK: { |
2160 | + 'key-mgmt': 'wpa-psk', |
2161 | + 'auth-alg': 'open' |
2162 | + }, |
2163 | + } |
2164 | + |
2165 | + |
2166 | +class NM80211ApFlags: |
2167 | + '''Device flags |
2168 | + |
2169 | + As per https://developer.gnome.org/NetworkManager/unstable/spec.html# |
2170 | + type-NM_802_11_AP_FLAGS |
2171 | + ''' |
2172 | + NM_802_11_AP_FLAGS_NONE = 0x00000000 |
2173 | + NM_802_11_AP_FLAGS_PRIVACY = 0x00000001 |
2174 | + |
2175 | + |
2176 | +def activate_connection(self, conn, dev, ap): |
2177 | + name = ap.rsplit('/', 1)[1] |
2178 | + RemoveActiveConnection( |
2179 | + self, dev, '/org/freedesktop/NetworkManager/ActiveConnection/' + name) |
2180 | + |
2181 | + state = dbus.UInt32( |
2182 | + NMActiveConnectionState.NM_ACTIVE_CONNECTION_STATE_ACTIVATED) |
2183 | + active_conn = dbus.ObjectPath( |
2184 | + AddActiveConnection(self, [dev], conn, ap, name, state)) |
2185 | + |
2186 | + return active_conn |
2187 | + |
2188 | + |
2189 | +def deactivate_connection(self, active_conn_path): |
2190 | + NM = dbusmock.get_object(MAIN_OBJ) |
2191 | + |
2192 | + for dev_path in NM.GetDevices(): |
2193 | + RemoveActiveConnection(self, dev_path, active_conn_path) |
2194 | + |
2195 | + |
2196 | +def add_and_activate_connection(self, conn_conf, dev, ap): |
2197 | + name = ap.rsplit('/', 1)[1] |
2198 | + RemoveWifiConnection( |
2199 | + self, dev, '/org/freedesktop/NetworkManager/Settings/' + name) |
2200 | + |
2201 | + raw_ssid = ''.join([ |
2202 | + chr(byte) for byte in conn_conf["802-11-wireless"]["ssid"]]) |
2203 | + wifi_conn = dbus.ObjectPath( |
2204 | + AddWiFiConnection(self, dev, name, raw_ssid, "")) |
2205 | + |
2206 | + active_conn = activate_connection(self, wifi_conn, dev, ap) |
2207 | + |
2208 | + return (wifi_conn, active_conn) |
2209 | + |
2210 | + |
2211 | +def load(mock, parameters): |
2212 | + mock.activate_connection = activate_connection |
2213 | + mock.deactivate_connection = deactivate_connection |
2214 | + mock.add_and_activate_connection = add_and_activate_connection |
2215 | + mock.AddMethods(MAIN_IFACE, [ |
2216 | + ('GetDevices', '', 'ao', |
2217 | + 'ret = [k for k in objects.keys() if "/Devices" in k]'), |
2218 | + ('GetPermissions', '', 'a{ss}', 'ret = {}'), |
2219 | + ('state', '', 'u', "ret = self.Get('%s', 'State')" % MAIN_IFACE), |
2220 | + ( |
2221 | + 'CheckConnectivity', '', 'u', |
2222 | + "ret = self.Get('%s', 'Connectivity')" % MAIN_IFACE), |
2223 | + ( |
2224 | + 'ActivateConnection', 'ooo', 'o', |
2225 | + "ret = self.activate_connection(self, args[0], args[1], args[2])"), |
2226 | + ( |
2227 | + 'DeactivateConnection', 'o', '', |
2228 | + "self.deactivate_connection(self, args[0])"), |
2229 | + ( |
2230 | + 'AddAndActivateConnection', 'a{sa{sv}}oo', 'oo', |
2231 | + "ret = self.add_and_activate_connection(" |
2232 | + "self, args[0], args[1], args[2])"), |
2233 | + ]) |
2234 | + |
2235 | + mock.AddProperties( |
2236 | + '', |
2237 | + { |
2238 | + 'ActiveConnections': dbus.Array([], signature='o'), |
2239 | + 'Devices': dbus.Array([], signature='o'), |
2240 | + 'NetworkingEnabled': parameters.get('NetworkingEnabled', True), |
2241 | + 'Connectivity': parameters.get( |
2242 | + 'Connectivity', |
2243 | + dbus.UInt32(NMConnectivityState.NM_CONNECTIVITY_FULL)), |
2244 | + 'State': parameters.get( |
2245 | + 'State', dbus.UInt32(NMState.NM_STATE_CONNECTED_GLOBAL)), |
2246 | + 'Startup': False, |
2247 | + 'Version': parameters.get('Version', '0.9.6.0'), |
2248 | + 'WimaxEnabled': parameters.get('WimaxEnabled', True), |
2249 | + 'WimaxHardwareEnabled': parameters.get( |
2250 | + 'WimaxHardwareEnabled', True), |
2251 | + 'WirelessEnabled': parameters.get('WirelessEnabled', True), |
2252 | + 'WirelessHardwareEnabled': parameters.get( |
2253 | + 'WirelessHardwareEnabled', True), |
2254 | + 'WwanEnabled': parameters.get('WwanEnabled', False), |
2255 | + 'WwanHardwareEnabled': parameters.get('WwanHardwareEnabled', True) |
2256 | + }) |
2257 | + |
2258 | + settings_props = {'Hostname': 'hostname', |
2259 | + 'CanModify': True, |
2260 | + 'Connections': dbus.Array([], signature='o')} |
2261 | + settings_methods = [( |
2262 | + 'ListConnections', '', 'ao', |
2263 | + "ret = self.Get('%s', 'Connections')" % |
2264 | + SETTINGS_IFACE), |
2265 | + ('GetConnectionByUuid', 's', 'o', ''), |
2266 | + ( |
2267 | + 'AddConnection', 'a{sa{sv}}', 'o', |
2268 | + 'ret = self.SettingsAddConnection(args[0])'), |
2269 | + ('SaveHostname', 's', '', '')] |
2270 | + mock.AddObject(SETTINGS_OBJ, |
2271 | + SETTINGS_IFACE, |
2272 | + settings_props, |
2273 | + settings_methods) |
2274 | + |
2275 | + |
2276 | +@dbus.service.method(MOCK_IFACE, |
2277 | + in_signature='sssv', out_signature='') |
2278 | +def SetProperty(self, path, iface, name, value): |
2279 | + obj = dbusmock.get_object(path) |
2280 | + obj.Set(iface, name, value) |
2281 | + obj.EmitSignal(iface, 'PropertiesChanged', 'a{sv}', [{name: value}]) |
2282 | + |
2283 | + |
2284 | +@dbus.service.method(MOCK_IFACE, |
2285 | + in_signature='u', out_signature='') |
2286 | +def SetGlobalConnectionState(self, state): |
2287 | + self.SetProperty(MAIN_OBJ, MAIN_IFACE, 'State', |
2288 | + dbus.UInt32(state, variant_level=1)) |
2289 | + self.EmitSignal(MAIN_IFACE, 'StateChanged', 'u', [state]) |
2290 | + |
2291 | + |
2292 | +@dbus.service.method(MOCK_IFACE, |
2293 | + in_signature='u', out_signature='') |
2294 | +def SetConnectivity(self, connectivity): |
2295 | + self.SetProperty(MAIN_OBJ, MAIN_IFACE, 'Connectivity', |
2296 | + dbus.UInt32(connectivity, variant_level=1)) |
2297 | + |
2298 | + |
2299 | +@dbus.service.method(MOCK_IFACE, |
2300 | + in_signature='ss', out_signature='') |
2301 | +def SetDeviceActive(self, device_path, active_connection_path): |
2302 | + dev_obj = dbusmock.get_object(device_path) |
2303 | + dev_obj.Set(DEVICE_IFACE, 'ActiveConnection', |
2304 | + dbus.ObjectPath(active_connection_path)) |
2305 | + old_state = dev_obj.Get(DEVICE_IFACE, 'State') |
2306 | + dev_obj.Set(DEVICE_IFACE, 'State', dbus.UInt32(DeviceState.ACTIVATED)) |
2307 | + |
2308 | + dev_obj.EmitSignal(DEVICE_IFACE, 'StateChanged', 'uuu', [ |
2309 | + dbus.UInt32(DeviceState.ACTIVATED), old_state, dbus.UInt32(1)]) |
2310 | + |
2311 | + |
2312 | +@dbus.service.method(MOCK_IFACE, |
2313 | + in_signature='s', out_signature='') |
2314 | +def SetDeviceDisconnected(self, device_path): |
2315 | + dev_obj = dbusmock.get_object(device_path) |
2316 | + dev_obj.Set(DEVICE_IFACE, 'ActiveConnection', dbus.ObjectPath('/')) |
2317 | + old_state = dev_obj.Get(DEVICE_IFACE, 'State') |
2318 | + dev_obj.Set(DEVICE_IFACE, 'State', dbus.UInt32(DeviceState.DISCONNECTED)) |
2319 | + |
2320 | + dev_obj.EmitSignal(DEVICE_IFACE, 'StateChanged', 'uuu', [ |
2321 | + dbus.UInt32(DeviceState.DISCONNECTED), old_state, dbus.UInt32(1)]) |
2322 | + |
2323 | + |
2324 | +@dbus.service.method(MOCK_IFACE, |
2325 | + in_signature='ssi', out_signature='s') |
2326 | +def AddEthernetDevice(self, device_name, iface_name, state): |
2327 | + '''Add an ethernet device. |
2328 | + |
2329 | + You have to specify device_name, device interface name (e. g. eth0), and |
2330 | + state. You can use the predefined DeviceState values (e. g. |
2331 | + DeviceState.ACTIVATED) or supply a numeric value. For valid state values |
2332 | + please visit |
2333 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html# |
2334 | + type-NM_DEVICE_STATE |
2335 | + |
2336 | + Please note that this does not set any global properties. |
2337 | + |
2338 | + Returns the new object path. |
2339 | + ''' |
2340 | + path = '/org/freedesktop/NetworkManager/Devices/' + device_name |
2341 | + wired_props = {'Carrier': False, |
2342 | + 'HwAddress': dbus.String('78:DD:08:D2:3D:43'), |
2343 | + 'PermHwAddress': dbus.String('78:DD:08:D2:3D:43'), |
2344 | + 'Speed': dbus.UInt32(0)} |
2345 | + self.AddObject(path, |
2346 | + 'org.freedesktop.NetworkManager.Device.Wired', |
2347 | + wired_props, |
2348 | + []) |
2349 | + |
2350 | + props = {'DeviceType': dbus.UInt32(1), |
2351 | + 'State': dbus.UInt32(state), |
2352 | + 'Interface': iface_name, |
2353 | + 'AvailableConnections': dbus.Array([], signature='o'), |
2354 | + 'IpInterface': ''} |
2355 | + |
2356 | + obj = dbusmock.get_object(path) |
2357 | + obj.AddProperties(DEVICE_IFACE, props) |
2358 | + |
2359 | + devices = self.Get(MAIN_IFACE, 'Devices') |
2360 | + devices.append(path) |
2361 | + self.Set(MAIN_IFACE, 'Devices', devices) |
2362 | + |
2363 | + self.EmitSignal('org.freedesktop.NetworkManager', 'DeviceAdded', 'o', |
2364 | + [path]) |
2365 | + |
2366 | + return path |
2367 | + |
2368 | + |
2369 | +@dbus.service.method(MOCK_IFACE, |
2370 | + in_signature='ssi', out_signature='s') |
2371 | +def AddWiFiDevice(self, device_name, iface_name, state): |
2372 | + '''Add a WiFi Device. |
2373 | + |
2374 | + You have to specify device_name, device interface name (e. g. wlan0) and |
2375 | + state. You can use the predefined DeviceState values (e. g. |
2376 | + DeviceState.ACTIVATED) or supply a numeric value. For valid state values, |
2377 | + please visit |
2378 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html# |
2379 | + type-NM_DEVICE_STATE |
2380 | + |
2381 | + Please note that this does not set any global properties. |
2382 | + |
2383 | + Returns the new object path. |
2384 | + ''' |
2385 | + |
2386 | + path = '/org/freedesktop/NetworkManager/Devices/' + device_name |
2387 | + self.AddObject(path, |
2388 | + WIRELESS_DEVICE_IFACE, |
2389 | + { |
2390 | + 'HwAddress': dbus.String('11:22:33:44:55:66'), |
2391 | + 'PermHwAddress': dbus.String('11:22:33:44:55:66'), |
2392 | + 'Bitrate': dbus.UInt32(5400), |
2393 | + 'Mode': dbus.UInt32(2), |
2394 | + 'WirelessCapabilities': dbus.UInt32(255), |
2395 | + 'AccessPoints': dbus.Array([], signature='o'), |
2396 | + }, |
2397 | + [ |
2398 | + ('GetAccessPoints', '', 'ao', |
2399 | + 'ret = self.access_points'), |
2400 | + ('GetAllAccessPoints', '', 'ao', |
2401 | + 'ret = self.access_points'), |
2402 | + ('RequestScan', 'a{sv}', '', ''), |
2403 | + ]) |
2404 | + |
2405 | + dev_obj = dbusmock.get_object(path) |
2406 | + dev_obj.access_points = [] |
2407 | + dev_obj.AddProperties(DEVICE_IFACE, |
2408 | + { |
2409 | + 'ActiveConnection': dbus.ObjectPath('/'), |
2410 | + 'AvailableConnections': |
2411 | + dbus.Array([], signature='o'), |
2412 | + 'AutoConnect': False, |
2413 | + 'Managed': True, |
2414 | + 'Driver': 'dbusmock', |
2415 | + 'DeviceType': dbus.UInt32(2), |
2416 | + 'State': dbus.UInt32(state), |
2417 | + 'Interface': iface_name, |
2418 | + 'IpInterface': iface_name, |
2419 | + }) |
2420 | + |
2421 | + devices = self.Get(MAIN_IFACE, 'Devices') |
2422 | + devices.append(path) |
2423 | + self.Set(MAIN_IFACE, 'Devices', devices) |
2424 | + |
2425 | + self.EmitSignal('org.freedesktop.NetworkManager', 'DeviceAdded', 'o', |
2426 | + [path]) |
2427 | + |
2428 | + return path |
2429 | + |
2430 | + |
2431 | +@dbus.service.method(MOCK_IFACE, |
2432 | + in_signature='ssssuuuyu', out_signature='s') |
2433 | +def AddAccessPoint(self, dev_path, ap_name, ssid, hw_address, |
2434 | + mode, frequency, rate, strength, security): |
2435 | + '''Add an access point to an existing WiFi device. |
2436 | + |
2437 | + You have to specify WiFi Device path, Access Point object name, |
2438 | + ssid, hw_address, mode, frequency, rate, strength and security. |
2439 | + For valid access point property values, please visit |
2440 | + http://projects.gnome.org/NetworkManager/developers/api/09/spec.html# |
2441 | + org.freedesktop.NetworkManager.AccessPoint |
2442 | + |
2443 | + Please note that this does not set any global properties. |
2444 | + |
2445 | + Returns the new object path. |
2446 | + ''' |
2447 | + dev_obj = dbusmock.get_object(dev_path) |
2448 | + ap_path = '/org/freedesktop/NetworkManager/AccessPoint/' + ap_name |
2449 | + if ap_path in dev_obj.access_points: |
2450 | + raise dbus.exceptions.DBusException( |
2451 | + 'Access point %s on device %s already exists' % (ap_name, |
2452 | + dev_path), |
2453 | + name=MAIN_IFACE + '.AlreadyExists') |
2454 | + |
2455 | + flags = NM80211ApFlags.NM_802_11_AP_FLAGS_PRIVACY |
2456 | + if security == NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE: |
2457 | + flags = NM80211ApFlags.NM_802_11_AP_FLAGS_NONE |
2458 | + |
2459 | + self.AddObject(ap_path, |
2460 | + ACCESS_POINT_IFACE, |
2461 | + {'Ssid': dbus.ByteArray(ssid.encode('UTF-8')), |
2462 | + 'HwAddress': dbus.String(hw_address), |
2463 | + 'Flags': dbus.UInt32(flags), |
2464 | + 'LastSeen': dbus.Int32(1), |
2465 | + 'Frequency': dbus.UInt32(frequency), |
2466 | + 'MaxBitrate': dbus.UInt32(rate), |
2467 | + 'Mode': dbus.UInt32(mode), |
2468 | + 'RsnFlags': dbus.UInt32(security), |
2469 | + 'WpaFlags': dbus.UInt32(security), |
2470 | + 'Strength': dbus.Byte(strength)}, |
2471 | + []) |
2472 | + |
2473 | + dev_obj.access_points.append(ap_path) |
2474 | + |
2475 | + aps = dev_obj.Get(WIRELESS_DEVICE_IFACE, 'AccessPoints') |
2476 | + aps.append(ap_path) |
2477 | + dev_obj.Set(WIRELESS_DEVICE_IFACE, 'AccessPoints', aps) |
2478 | + |
2479 | + dev_obj.EmitSignal(WIRELESS_DEVICE_IFACE, 'AccessPointAdded', 'o', |
2480 | + [ap_path]) |
2481 | + |
2482 | + return ap_path |
2483 | + |
2484 | + |
2485 | +@dbus.service.method(MOCK_IFACE, |
2486 | + in_signature='ssss', out_signature='s') |
2487 | +def AddWiFiConnection(self, dev_path, connection_name, ssid_name, key_mgmt): |
2488 | + '''Add an available connection to an existing WiFi device and access point. |
2489 | + |
2490 | + You have to specify WiFi Device path, Connection object name, |
2491 | + SSID and key management. |
2492 | + |
2493 | + The SSID must match one of the previously created access points. |
2494 | + |
2495 | + Please note that this does not set any global properties. |
2496 | + |
2497 | + Returns the new object path. |
2498 | + ''' |
2499 | + |
2500 | + dev_obj = dbusmock.get_object(dev_path) |
2501 | + connection_path = ('/org/freedesktop/NetworkManager/Settings/%s' % |
2502 | + connection_name) |
2503 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
2504 | + |
2505 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2506 | + main_connections = settings_obj.ListConnections() |
2507 | + |
2508 | + ssid = ssid_name.encode('UTF-8') |
2509 | + |
2510 | + # Find the access point by ssid |
2511 | + access_point = None |
2512 | + access_points = dev_obj.access_points |
2513 | + for ap_path in access_points: |
2514 | + ap = dbusmock.get_object(ap_path) |
2515 | + if ap.Get(ACCESS_POINT_IFACE, 'Ssid') == ssid: |
2516 | + access_point = ap |
2517 | + break |
2518 | + |
2519 | + if not access_point: |
2520 | + raise dbus.exceptions.DBusException( |
2521 | + 'Access point with SSID [%s] could not be found' % (ssid_name), |
2522 | + name=MAIN_IFACE + '.DoesNotExist') |
2523 | + |
2524 | + hw_address = access_point.Get(ACCESS_POINT_IFACE, 'HwAddress') |
2525 | + mode = access_point.Get(ACCESS_POINT_IFACE, 'Mode') |
2526 | + security = access_point.Get(ACCESS_POINT_IFACE, 'WpaFlags') |
2527 | + |
2528 | + if connection_path in connections or connection_path in main_connections: |
2529 | + raise dbus.exceptions.DBusException( |
2530 | + 'Connection %s on device %s already exists' % ( |
2531 | + connection_name, dev_path), |
2532 | + name=MAIN_IFACE + '.AlreadyExists') |
2533 | + |
2534 | + # Parse mac address string into byte array |
2535 | + mac_bytes = binascii.unhexlify(hw_address.replace(':', '')) |
2536 | + |
2537 | + settings = { |
2538 | + '802-11-wireless': { |
2539 | + 'seen-bssids': [hw_address], |
2540 | + 'ssid': dbus.ByteArray(ssid), |
2541 | + 'mac-address': dbus.ByteArray(mac_bytes), |
2542 | + 'mode': InfrastructureMode.NAME_MAP[mode] |
2543 | + }, |
2544 | + 'connection': { |
2545 | + 'timestamp': dbus.UInt64(1374828522), |
2546 | + 'type': '802-11-wireless', |
2547 | + 'id': ssid_name, |
2548 | + 'uuid': str(uuid.uuid4()) |
2549 | + }, |
2550 | + } |
2551 | + |
2552 | + if security != NM80211ApSecurityFlags.NM_802_11_AP_SEC_NONE: |
2553 | + settings['802-11-wireless']['security'] = '802-11-wireless-security' |
2554 | + sec = NM80211ApSecurityFlags.NAME_MAP[security] |
2555 | + settings['802-11-wireless-security'] = sec |
2556 | + |
2557 | + self.AddObject(connection_path, |
2558 | + CSETTINGS_IFACE, |
2559 | + { |
2560 | + 'Settings': dbus.Dictionary( |
2561 | + settings, signature='sa{sv}'), |
2562 | + 'Secrets': dbus.Dictionary({}, signature='sa{sv}'), |
2563 | + }, |
2564 | + [ |
2565 | + ( |
2566 | + 'Delete', '', '', |
2567 | + 'self.ConnectionDelete("%s")' % connection_path), |
2568 | + ( |
2569 | + 'GetSettings', '', 'a{sa{sv}}', |
2570 | + "ret = self.Get('%s', 'Settings')" % |
2571 | + CSETTINGS_IFACE), |
2572 | + ( |
2573 | + 'GetSecrets', 's', 'a{sa{sv}}', |
2574 | + "ret = self.Get('%s', 'Secrets')" % |
2575 | + CSETTINGS_IFACE), |
2576 | + ( |
2577 | + 'Update', 'a{sa{sv}}', '', |
2578 | + 'self.ConnectionUpdate("%s", args[0])' % |
2579 | + connection_path), |
2580 | + ]) |
2581 | + |
2582 | + connections.append(dbus.ObjectPath(connection_path)) |
2583 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
2584 | + |
2585 | + main_connections.append(connection_path) |
2586 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2587 | + |
2588 | + settings_obj.EmitSignal(SETTINGS_IFACE, 'NewConnection', 'o', [ap_path]) |
2589 | + |
2590 | + return connection_path |
2591 | + |
2592 | + |
2593 | +@dbus.service.method(MOCK_IFACE, |
2594 | + in_signature='assssu', out_signature='s') |
2595 | +def AddActiveConnection(self, devices, connection_device, specific_object, |
2596 | + name, state): |
2597 | + '''Add an active connection to an existing WiFi device. |
2598 | + |
2599 | + You have to a list of the involved WiFi devices, the connection path, |
2600 | + the access point path, ActiveConnection object name and connection |
2601 | + state. |
2602 | + |
2603 | + Please note that this does not set any global properties. |
2604 | + |
2605 | + Returns the new object path. |
2606 | + ''' |
2607 | + |
2608 | + conn_obj = dbusmock.get_object(connection_device) |
2609 | + settings = conn_obj.Get(CSETTINGS_IFACE, 'Settings') |
2610 | + conn_uuid = settings['connection']['uuid'] |
2611 | + |
2612 | + device_objects = [dbus.ObjectPath(dev) for dev in devices] |
2613 | + |
2614 | + active_connection_path = ('/org/freedesktop/NetworkManager/' |
2615 | + 'ActiveConnection/%s' % name) |
2616 | + self.AddObject(active_connection_path, |
2617 | + ACTIVE_CONNECTION_IFACE, |
2618 | + { |
2619 | + 'Devices': device_objects, |
2620 | + 'Default6': False, |
2621 | + 'Default': True, |
2622 | + 'Vpn': False, |
2623 | + 'Connection': dbus.ObjectPath(connection_device), |
2624 | + 'Master': dbus.ObjectPath('/'), |
2625 | + 'SpecificObject': dbus.ObjectPath(specific_object), |
2626 | + 'Uuid': conn_uuid, |
2627 | + 'State': state, |
2628 | + }, |
2629 | + []) |
2630 | + |
2631 | + for dev_path in devices: |
2632 | + self.SetDeviceActive(dev_path, active_connection_path) |
2633 | + |
2634 | + active_connections = self.Get(MAIN_IFACE, 'ActiveConnections') |
2635 | + active_connections.append(dbus.ObjectPath(active_connection_path)) |
2636 | + self.SetProperty(MAIN_OBJ, MAIN_IFACE, 'ActiveConnections', |
2637 | + active_connections) |
2638 | + |
2639 | + return active_connection_path |
2640 | + |
2641 | + |
2642 | +@dbus.service.method(MOCK_IFACE, |
2643 | + in_signature='ss', out_signature='') |
2644 | +def RemoveAccessPoint(self, dev_path, ap_path): |
2645 | + '''Remove the specified access point. |
2646 | + |
2647 | + You have to specify the device to remove the access point from, and the |
2648 | + path of the access point. |
2649 | + |
2650 | + Please note that this does not set any global properties. |
2651 | + ''' |
2652 | + |
2653 | + dev_obj = dbusmock.get_object(dev_path) |
2654 | + |
2655 | + aps = dev_obj.Get(WIRELESS_DEVICE_IFACE, 'AccessPoints') |
2656 | + aps.remove(ap_path) |
2657 | + dev_obj.Set(WIRELESS_DEVICE_IFACE, 'AccessPoints', aps) |
2658 | + |
2659 | + dev_obj.access_points.remove(ap_path) |
2660 | + |
2661 | + dev_obj.EmitSignal(WIRELESS_DEVICE_IFACE, 'AccessPointRemoved', 'o', |
2662 | + [ap_path]) |
2663 | + |
2664 | + self.RemoveObject(ap_path) |
2665 | + |
2666 | + |
2667 | +@dbus.service.method(MOCK_IFACE, |
2668 | + in_signature='ss', out_signature='') |
2669 | +def RemoveWifiConnection(self, dev_path, connection_path): |
2670 | + '''Remove the specified WiFi connection. |
2671 | + |
2672 | + You have to specify the device to remove the connection from, and the |
2673 | + path of the Connection. |
2674 | + |
2675 | + Please note that this does not set any global properties. |
2676 | + ''' |
2677 | + |
2678 | + dev_obj = dbusmock.get_object(dev_path) |
2679 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2680 | + |
2681 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
2682 | + main_connections = settings_obj.ListConnections() |
2683 | + |
2684 | + if (connection_path not in connections and |
2685 | + connection_path not in main_connections): |
2686 | + return |
2687 | + |
2688 | + connections.remove(dbus.ObjectPath(connection_path)) |
2689 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
2690 | + |
2691 | + main_connections.remove(connection_path) |
2692 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2693 | + |
2694 | + settings_obj.EmitSignal(SETTINGS_IFACE, 'ConnectionRemoved', 'o', |
2695 | + [connection_path]) |
2696 | + |
2697 | + connection_obj = dbusmock.get_object(connection_path) |
2698 | + connection_obj.EmitSignal(CSETTINGS_IFACE, 'Removed', '', []) |
2699 | + |
2700 | + self.RemoveObject(connection_path) |
2701 | + |
2702 | + |
2703 | +@dbus.service.method(MOCK_IFACE, |
2704 | + in_signature='ss', out_signature='') |
2705 | +def RemoveActiveConnection(self, dev_path, active_connection_path): |
2706 | + '''Remove the specified ActiveConnection. |
2707 | + |
2708 | + You have to specify the device to remove the connection from, and the |
2709 | + path of the ActiveConnection. |
2710 | + |
2711 | + Please note that this does not set any global properties. |
2712 | + ''' |
2713 | + self.SetDeviceDisconnected(dev_path) |
2714 | + |
2715 | + active_connections = self.Get(MAIN_IFACE, 'ActiveConnections') |
2716 | + |
2717 | + if active_connection_path not in active_connections: |
2718 | + return |
2719 | + |
2720 | + active_connections.remove(dbus.ObjectPath(active_connection_path)) |
2721 | + self.SetProperty(MAIN_OBJ, MAIN_IFACE, 'ActiveConnections', |
2722 | + active_connections) |
2723 | + |
2724 | + self.RemoveObject(active_connection_path) |
2725 | + |
2726 | + |
2727 | +@dbus.service.method(SETTINGS_IFACE, |
2728 | + in_signature='a{sa{sv}}', out_signature='o') |
2729 | +def SettingsAddConnection(self, connection_settings): |
2730 | + '''Add a connection. |
2731 | + |
2732 | + connection_settings is a String String Variant Map Map. See |
2733 | + https://developer.gnome.org/NetworkManager/0.9/spec.html |
2734 | + #type-String_String_Variant_Map_Map |
2735 | + |
2736 | + If you omit uuid, this method adds one for you. |
2737 | + ''' |
2738 | + |
2739 | + if 'uuid' not in connection_settings['connection']: |
2740 | + connection_settings['connection']['uuid'] = str(uuid.uuid4()) |
2741 | + |
2742 | + NM = dbusmock.get_object(MAIN_OBJ) |
2743 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2744 | + main_connections = settings_obj.ListConnections() |
2745 | + |
2746 | + # Mimic how NM names connections |
2747 | + connection_name = str(len(main_connections)) |
2748 | + |
2749 | + connection_path = SETTINGS_OBJ + '/' + connection_name |
2750 | + |
2751 | + if connection_path in main_connections: |
2752 | + raise dbus.exceptions.DBusException( |
2753 | + 'Connection %s already exists' % connection_path, |
2754 | + name=MAIN_IFACE + '.AlreadyExists',) |
2755 | + |
2756 | + self.AddObject(connection_path, |
2757 | + CSETTINGS_IFACE, |
2758 | + { |
2759 | + 'Settings': dbus.Dictionary(connection_settings, |
2760 | + signature='sa{sv}'), |
2761 | + 'Secrets': dbus.Dictionary({}, signature='sa{sv}'), |
2762 | + }, |
2763 | + [ |
2764 | + ( |
2765 | + 'Delete', '', '', |
2766 | + 'self.ConnectionDelete("%s")' % connection_path), |
2767 | + ( |
2768 | + 'GetSettings', '', 'a{sa{sv}}', |
2769 | + "ret = self.Get('%s', 'Settings')" % |
2770 | + CSETTINGS_IFACE), |
2771 | + ( |
2772 | + 'GetSecrets', 's', 'a{sa{sv}}', |
2773 | + "ret = self.Get('%s', 'Secrets')" % |
2774 | + CSETTINGS_IFACE), |
2775 | + ( |
2776 | + 'Update', 'a{sa{sv}}', '', |
2777 | + 'self.ConnectionUpdate("%s", args[0])' % |
2778 | + connection_path), |
2779 | + ]) |
2780 | + |
2781 | + main_connections.append(connection_path) |
2782 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2783 | + |
2784 | + settings_obj.EmitSignal(SETTINGS_IFACE, 'NewConnection', 'o', |
2785 | + [connection_path]) |
2786 | + |
2787 | + auto_connect = False |
2788 | + if 'autoconnect' in connection_settings['connection']: |
2789 | + auto_connect = connection_settings['connection']['autoconnect'] |
2790 | + |
2791 | + if auto_connect: |
2792 | + dev = None |
2793 | + devices = NM.GetDevices() |
2794 | + |
2795 | + # Grab the first device. |
2796 | + if len(devices) > 0: |
2797 | + dev = devices[0] |
2798 | + |
2799 | + if dev: |
2800 | + activate_connection(NM, connection_path, dev, connection_path) |
2801 | + |
2802 | + return connection_path |
2803 | + |
2804 | + |
2805 | +@dbus.service.method(CSETTINGS_IFACE, |
2806 | + in_signature='oa{sa{sv}}', out_signature='') |
2807 | +def ConnectionUpdate(self, connection_path, settings): |
2808 | + '''Update settings on a connection. |
2809 | + |
2810 | + settings is a String String Variant Map Map. See |
2811 | + https://developer.gnome.org/NetworkManager/0.9/spec.html |
2812 | + #type-String_String_Variant_Map_Map |
2813 | + ''' |
2814 | + NM = dbusmock.get_object(MAIN_OBJ) |
2815 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2816 | + conn_obj = dbusmock.get_object(connection_path) |
2817 | + |
2818 | + main_connections = settings_obj.ListConnections() |
2819 | + |
2820 | + if connection_path not in main_connections: |
2821 | + raise dbus.exceptions.DBusException( |
2822 | + 'Connection %s does not exist' % connection_path, |
2823 | + name=MAIN_IFACE + '.DoesNotExist',) |
2824 | + |
2825 | + conn_settings = conn_obj.Get(CSETTINGS_IFACE, 'Settings') |
2826 | + changed_settings = {} |
2827 | + for key, value in settings.items(): |
2828 | + for k, v in value.items(): |
2829 | + changed_settings[k] = v |
2830 | + |
2831 | + if key not in conn_settings: |
2832 | + conn_settings[key] = dbus.Dictionary({}, signature='sv') |
2833 | + |
2834 | + conn_settings[key][k] = v |
2835 | + |
2836 | + conn_obj.Set(CSETTINGS_IFACE, 'Settings', conn_settings) |
2837 | + |
2838 | + settings_obj.EmitSignal(CSETTINGS_IFACE, 'PropertiesChanged', 'a{sv}', |
2839 | + [changed_settings]) |
2840 | + settings_obj.EmitSignal(CSETTINGS_IFACE, 'Updated', '', []) |
2841 | + |
2842 | + auto_connect = False |
2843 | + if 'autoconnect' in settings['connection']: |
2844 | + auto_connect = settings['connection']['autoconnect'] |
2845 | + |
2846 | + if auto_connect: |
2847 | + dev = None |
2848 | + devices = NM.GetDevices() |
2849 | + |
2850 | + # Grab the first device. |
2851 | + if len(devices) > 0: |
2852 | + dev = devices[0] |
2853 | + |
2854 | + if dev: |
2855 | + activate_connection(NM, connection_path, dev, connection_path) |
2856 | + |
2857 | + return connection_path |
2858 | + |
2859 | + |
2860 | +@dbus.service.method(CSETTINGS_IFACE, |
2861 | + in_signature='o', out_signature='') |
2862 | +def ConnectionDelete(self, connection_path): |
2863 | + '''Deletes a connection. |
2864 | + |
2865 | + This also |
2866 | + * removes the deleted connection from any device, |
2867 | + * removes any active connection(s) it might be associated with, |
2868 | + * removes it from the Settings interface, |
2869 | + * as well as deletes the object from the mock. |
2870 | + |
2871 | + Note: If this was the only active connection, we change the global |
2872 | + connection state. |
2873 | + ''' |
2874 | + NM = dbusmock.get_object(MAIN_OBJ) |
2875 | + settings_obj = dbusmock.get_object(SETTINGS_OBJ) |
2876 | + |
2877 | + # Find the associated active connection(s). |
2878 | + active_connections = NM.Get(MAIN_IFACE, 'ActiveConnections') |
2879 | + associated_active_connections = [] |
2880 | + for ac in active_connections: |
2881 | + ac_obj = dbusmock.get_object(ac) |
2882 | + ac_con = ac_obj.Get(ACTIVE_CONNECTION_IFACE, 'Connection') |
2883 | + if ac_con == connection_path: |
2884 | + associated_active_connections.append(ac) |
2885 | + |
2886 | + # We found that the connection we are deleting are associated to all |
2887 | + # active connections and subsequently set the global state to |
2888 | + # disconnected. |
2889 | + if len(active_connections) == len(associated_active_connections): |
2890 | + self.SetGlobalConnectionState(NMState.NM_STATE_DISCONNECTED) |
2891 | + |
2892 | + # Remove the connection from all associated devices. |
2893 | + # We also remove all associated active connections. |
2894 | + for dev_path in NM.GetDevices(): |
2895 | + dev_obj = dbusmock.get_object(dev_path) |
2896 | + connections = dev_obj.Get(DEVICE_IFACE, 'AvailableConnections') |
2897 | + |
2898 | + for ac in associated_active_connections: |
2899 | + NM.RemoveActiveConnection(dev_path, ac) |
2900 | + |
2901 | + if connection_path not in connections: |
2902 | + continue |
2903 | + |
2904 | + connections.remove(dbus.ObjectPath(connection_path)) |
2905 | + dev_obj.Set(DEVICE_IFACE, 'AvailableConnections', connections) |
2906 | + |
2907 | + # Remove the connection from the settings interface |
2908 | + main_connections = settings_obj.ListConnections() |
2909 | + if connection_path not in main_connections: |
2910 | + return |
2911 | + main_connections.remove(connection_path) |
2912 | + settings_obj.Set(SETTINGS_IFACE, 'Connections', main_connections) |
2913 | + settings_obj.EmitSignal(SETTINGS_IFACE, 'ConnectionRemoved', 'o', |
2914 | + [connection_path]) |
2915 | + |
2916 | + # Remove the connection from the mock |
2917 | + connection_obj = dbusmock.get_object(connection_path) |
2918 | + connection_obj.EmitSignal(CSETTINGS_IFACE, 'Removed', '', []) |
2919 | + self.RemoveObject(connection_path) |
2920 | |
2921 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/test_cellular.py' |
2922 | --- tests/autopilot/ubuntu_system_settings/tests/test_cellular.py 2014-10-29 15:51:17 +0000 |
2923 | +++ tests/autopilot/ubuntu_system_settings/tests/test_cellular.py 2015-05-18 15:06:15 +0000 |
2924 | @@ -5,6 +5,7 @@ |
2925 | # under the terms of the GNU General Public License version 3, as published |
2926 | # by the Free Software Foundation. |
2927 | |
2928 | +import dbus |
2929 | from gi.repository import Gio, GLib |
2930 | from time import sleep |
2931 | |
2932 | @@ -13,7 +14,14 @@ |
2933 | from testtools.matchers import Equals, raises, StartsWith |
2934 | |
2935 | from ubuntu_system_settings.tests import ( |
2936 | - CellularBaseTestCase, CONNMAN_IFACE, RDO_IFACE, NETREG_IFACE) |
2937 | + CellularBaseTestCase, HotspotBaseTestCase, CONNMAN_IFACE, RDO_IFACE, |
2938 | + NETREG_IFACE) |
2939 | + |
2940 | +from ubuntu_system_settings.tests.networkmanager import ( |
2941 | + CSETTINGS_IFACE, MAIN_OBJ as NM_PATH, MAIN_IFACE as NM_IFACE, |
2942 | +) |
2943 | + |
2944 | +DEV_IFACE = 'org.freedesktop.NetworkManager.Device' |
2945 | |
2946 | |
2947 | class CellularTestCase(CellularBaseTestCase): |
2948 | @@ -288,3 +296,116 @@ |
2949 | lambda: |
2950 | gsettings.get_value('default-sim-for-messages').get_string(), |
2951 | Eventually(Equals('/ril_1'))) |
2952 | + |
2953 | + |
2954 | +class HotspotTestCase(HotspotBaseTestCase): |
2955 | + |
2956 | + def test_setup(self): |
2957 | + if not self.cellular_page.have_hotspot(): |
2958 | + self.skipTest('Cannot test hotspot since wifi is disabled.') |
2959 | + |
2960 | + ssid = 'Ubuntu' |
2961 | + password = 'abcdefgh' |
2962 | + config = {'password': password} |
2963 | + active_con_path = NM_PATH + '/ActiveConnection/0' |
2964 | + con_path = NM_PATH + '/Settings/0' |
2965 | + |
2966 | + hotspot_page = self.cellular_page.setup_hotspot(config) |
2967 | + |
2968 | + # Assert that the switch is on. |
2969 | + self.assertTrue(hotspot_page.get_hotspot_status()) |
2970 | + |
2971 | + # Assert that Block on Urfkill is called twice. |
2972 | + self.assertThat( |
2973 | + lambda: len(self.urfkill_mock.GetMethodCalls('Block')), |
2974 | + Eventually(Equals(2)) |
2975 | + ) |
2976 | + |
2977 | + # Assert that we get one active connection. |
2978 | + self.assertThat( |
2979 | + lambda: len(self.obj_nm.GetAll(NM_IFACE)['ActiveConnections']), |
2980 | + Eventually(Equals(1)) |
2981 | + ) |
2982 | + |
2983 | + # Assert that the active connection has a certain path. |
2984 | + self.assertThat( |
2985 | + lambda: self.obj_nm.GetAll(NM_IFACE)['ActiveConnections'][0], |
2986 | + Eventually(Equals(active_con_path)) |
2987 | + ) |
2988 | + |
2989 | + # Assert the device's active connection |
2990 | + self.assertThat( |
2991 | + lambda: self.device_mock.Get(DEV_IFACE, 'ActiveConnection'), |
2992 | + Eventually(Equals(active_con_path)) |
2993 | + ) |
2994 | + |
2995 | + connection_mock = dbus.Interface(self.dbus_con.get_object( |
2996 | + NM_IFACE, con_path), CSETTINGS_IFACE) |
2997 | + |
2998 | + settings = connection_mock.GetSettings() |
2999 | + |
3000 | + # Assert that autoconnect is true, that ssid and password is what we |
3001 | + # expect them to be. |
3002 | + self.assertTrue(settings['connection']['autoconnect']) |
3003 | + |
3004 | + s_ssid = bytearray(settings['802-11-wireless']['ssid']).decode('utf-8') |
3005 | + self.assertEqual(s_ssid, ssid) |
3006 | + self.assertEqual(settings['802-11-wireless-security']['psk'], password) |
3007 | + |
3008 | + def test_enabling(self): |
3009 | + if not self.cellular_page.have_hotspot(): |
3010 | + self.skipTest('Cannot test hotspot since wifi is disabled.') |
3011 | + |
3012 | + self.add_hotspot('foo', 'abcdefgh', enabled=False) |
3013 | + |
3014 | + self.assertThat( |
3015 | + lambda: len(self.obj_nm.GetAll(NM_IFACE)['ActiveConnections']), |
3016 | + Eventually(Equals(0)) |
3017 | + ) |
3018 | + |
3019 | + self.cellular_page.enable_hotspot() |
3020 | + |
3021 | + self.assertThat( |
3022 | + lambda: len(self.obj_nm.GetAll(NM_IFACE)['ActiveConnections']), |
3023 | + Eventually(Equals(1)) |
3024 | + ) |
3025 | + |
3026 | + def test_disabling(self): |
3027 | + if not self.cellular_page.have_hotspot(): |
3028 | + self.skipTest('Cannot test hotspot since wifi is disabled.') |
3029 | + |
3030 | + self.add_hotspot('foo', 'abcdefgh', enabled=True) |
3031 | + |
3032 | + self.assertThat( |
3033 | + lambda: len(self.obj_nm.GetAll(NM_IFACE)['ActiveConnections']), |
3034 | + Eventually(Equals(1)) |
3035 | + ) |
3036 | + |
3037 | + self.cellular_page.disable_hotspot() |
3038 | + |
3039 | + self.assertThat( |
3040 | + lambda: len(self.obj_nm.GetAll(NM_IFACE)['ActiveConnections']), |
3041 | + Eventually(Equals(0)) |
3042 | + ) |
3043 | + |
3044 | + def test_changing(self): |
3045 | + if not self.cellular_page.have_hotspot(): |
3046 | + self.skipTest('Cannot test hotspot since wifi is disabled.') |
3047 | + |
3048 | + con_path = self.add_hotspot('foo', 'abcdefgh', enabled=True) |
3049 | + |
3050 | + ssid = 'bar' |
3051 | + password = 'zomgzomg' |
3052 | + config = {'ssid': ssid, 'password': password} |
3053 | + self.cellular_page.setup_hotspot(config) |
3054 | + |
3055 | + con_path = NM_PATH + '/Settings/0' |
3056 | + |
3057 | + con_mock = dbus.Interface(self.dbus_con.get_object( |
3058 | + NM_IFACE, con_path), CSETTINGS_IFACE) |
3059 | + |
3060 | + settings = con_mock.GetSettings() |
3061 | + |
3062 | + s_ssid = bytearray(settings['802-11-wireless']['ssid']).decode('utf-8') |
3063 | + self.assertEqual(s_ssid, ssid) |
3064 | + self.assertEqual(settings['802-11-wireless-security']['psk'], password) |
3065 | |
3066 | === modified file 'tests/plugins/system-update/fakesystemupdate.h' |
3067 | --- tests/plugins/system-update/fakesystemupdate.h 2014-10-24 20:06:13 +0000 |
3068 | +++ tests/plugins/system-update/fakesystemupdate.h 2015-05-18 15:06:15 +0000 |
3069 | @@ -38,6 +38,7 @@ |
3070 | int currentBuildNumber() { return 123;} |
3071 | QString currentUbuntuBuildNumber() { return QString("20140927");} |
3072 | QString currentDeviceBuildNumber() { return QString("20140927");} |
3073 | + QString deviceName() { return QString("mako");} |
3074 | QDateTime lastUpdateDate() { return QDateTime::currentDateTime(); } |
3075 | |
3076 | void checkForUpdate() {} |
FAILED: Continuous integration, rev:1337 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- ci/2013/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 1695 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- vivid-i386- ci/283 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-mako/ 1503 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 1693 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 1693/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 18664
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- system- settings- ci/2013/ rebuild
http://