Merge lp:~jonas-drange/ubuntu-system-settings/hotspots-binding into lp:ubuntu-system-settings

Proposed by Jonas G. Drange
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
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-1/system.d/wpa_supplicant.conf # so it looks like this: http://pastebin.ubuntu.com/10576889/
2. Confirm you can call $ gdbus call --system -d fi.w1.wpa_supplicant1 -o /fi/w1/wpa_supplicant1 -m fi.w1.wpa_supplicant1.SetInterfaceFirmware / ap

All devices:
1. Edit /usr/share/applications/ubuntu-system-settings.desktop
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://bazaar.launchpad.net/~mathieu-tl/+junk/touch-hotspot/view/head:/hotspot.py

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

See a couple comments and questions inline.

review: Needs Information
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Information given, but sat MP back to WIP to work on what Ken pointed out.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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.

review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
1369. By Jonas G. Drange

style

1370. By Jonas G. Drange

skip if wifi disabled

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Needs Fixing
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 serverpropertysynchroniser

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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() {}

Subscribers

People subscribed via source and target branches