Merge lp:~ken-vandine/ubuntu-system-settings/security_panel into lp:ubuntu-system-settings
- security_panel
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ken VanDine |
Approved revision: | 934 |
Merged at revision: | 954 |
Proposed branch: | lp:~ken-vandine/ubuntu-system-settings/security_panel |
Merge into: | lp:ubuntu-system-settings |
Prerequisite: | lp:~jonas-drange/ubuntu-system-settings/visual-feedback-in-grid |
Diff against target: |
924 lines (+743/-27) 9 files modified
plugins/security-privacy/CMakeLists.txt (+3/-0) plugins/security-privacy/Ofono.qml (+38/-0) plugins/security-privacy/PageComponent.qml (+52/-4) plugins/security-privacy/SimPin.qml (+358/-0) plugins/security-privacy/sims.js (+34/-0) src/qml/CategoryGrid.qml (+1/-0) tests/autopilot/ubuntu_system_settings/tests/__init__.py (+5/-22) tests/autopilot/ubuntu_system_settings/tests/ofono.py (+34/-0) tests/autopilot/ubuntu_system_settings/tests/test_security.py (+218/-1) |
To merge this branch: | bzr merge lp:~ken-vandine/ubuntu-system-settings/security_panel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Jonas G. Drange (community) | Approve | ||
Review via email: mp+232478@code.launchpad.net |
This proposal supersedes a proposal from 2014-08-27.
Commit message
SIM PIN lock implementation
Description of the change
SIM PIN lock implementation
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:930
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
It works beautifully.
One small issue:
When changing the pin of a locked sim I get "undefined attempts remaining" – but it seems to work just fine. Skip "n attempts remaining" when locked?
Sebastien Bacher (seb128) wrote : Posted in a previous version of this proposal | # |
Thanks for the work, some issues
- you seem to have code for display effect on panel when clicked, merge error?
- clicking on the switch and doing "cancel" leads to the control going to "true", it should not
- it displays slots for non present SIMs
- toggling a SIM leads to a "Enter SIM PIN" titled dialog with a "Lock" button, the spec doesn't describe that case?
- 933. By Ken VanDine
-
ensure the checked state matches the locked state
- 934. By Ken VanDine
-
fixed title for unlock dialog to match design when unlocking
Jonas G. Drange (jonas-drange) wrote : Posted in a previous version of this proposal | # |
Retries seems broken on my SIM. I'll try to dig a bit deeper and report a bug against something.
Good stuff!
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:931
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Jonas G. Drange (jonas-drange) wrote : | # |
Retries seems broken on my SIM. I'll try to dig a bit deeper and report a bug against something.
Good stuff!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:932
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'plugins/security-privacy/CMakeLists.txt' |
2 | --- plugins/security-privacy/CMakeLists.txt 2014-08-11 11:05:58 +0000 |
3 | +++ plugins/security-privacy/CMakeLists.txt 2014-08-27 21:27:21 +0000 |
4 | @@ -22,8 +22,11 @@ |
5 | Dash.qml |
6 | Location.qml |
7 | LockSecurity.qml |
8 | + Ofono.qml |
9 | PageComponent.qml |
10 | PhoneLocking.qml |
11 | + SimPin.qml |
12 | + sims.js |
13 | ) |
14 | |
15 | set(PANEL_SOURCES |
16 | |
17 | === added file 'plugins/security-privacy/Ofono.qml' |
18 | --- plugins/security-privacy/Ofono.qml 1970-01-01 00:00:00 +0000 |
19 | +++ plugins/security-privacy/Ofono.qml 2014-08-27 21:27:21 +0000 |
20 | @@ -0,0 +1,38 @@ |
21 | +/* |
22 | + * Copyright (C) 2014 Canonical Ltd |
23 | + * |
24 | + * This program is free software: you can redistribute it and/or modify |
25 | + * it under the terms of the GNU General Public License version 3 as |
26 | + * published by the Free Software Foundation. |
27 | + * |
28 | + * This program is distributed in the hope that it will be useful, |
29 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31 | + * GNU General Public License for more details. |
32 | + * |
33 | + * You should have received a copy of the GNU General Public License |
34 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
35 | + * |
36 | + * Authors: |
37 | + * Jonas G. Drange <jonas.drange@canonical.com> |
38 | + * |
39 | +*/ |
40 | +import QtQuick 2.0 |
41 | +import MeeGo.QOfono 0.2 |
42 | + |
43 | +Item { |
44 | + property alias simMng: simMng |
45 | + property alias present: simMng.present |
46 | + |
47 | + property string path |
48 | + property string name |
49 | + property string title: { |
50 | + var number = simMng.subscriberNumbers[0] || simMng.subscriberIdentity; |
51 | + return name + (number ? " (" + number + ")" : ""); |
52 | + } |
53 | + |
54 | + OfonoSimManager { |
55 | + id: simMng |
56 | + modemPath: path |
57 | + } |
58 | +} |
59 | |
60 | === modified file 'plugins/security-privacy/PageComponent.qml' |
61 | --- plugins/security-privacy/PageComponent.qml 2014-08-26 00:54:38 +0000 |
62 | +++ plugins/security-privacy/PageComponent.qml 2014-08-27 21:27:21 +0000 |
63 | @@ -27,6 +27,8 @@ |
64 | import Ubuntu.SystemSettings.Battery 1.0 |
65 | import Ubuntu.SystemSettings.Diagnostics 1.0 |
66 | import Ubuntu.SystemSettings.SecurityPrivacy 1.0 |
67 | +import MeeGo.QOfono 0.2 |
68 | +import "sims.js" as Sims |
69 | |
70 | ItemPage { |
71 | id: root |
72 | @@ -37,6 +39,18 @@ |
73 | |
74 | property alias usePowerd: batteryBackend.powerdRunning |
75 | property bool lockOnSuspend |
76 | + property var modemsSorted: manager.modems.slice(0).sort() |
77 | + property var sims |
78 | + property int simsPresent: 0 |
79 | + property int simsLoaded: 0 |
80 | + property int simsLocked: { |
81 | + var t = 0; |
82 | + sims.forEach(function (sim) { |
83 | + if (sim.simMng.lockedPins.length > 0) |
84 | + t++; |
85 | + }); |
86 | + return t; |
87 | + } |
88 | |
89 | UbuntuDiagnostics { |
90 | id: diagnosticsWidget |
91 | @@ -50,6 +64,33 @@ |
92 | id: batteryBackend |
93 | } |
94 | |
95 | + OfonoManager { |
96 | + id: manager |
97 | + Component.onCompleted: { |
98 | + // create ofono bindings for all modem paths |
99 | + var component = Qt.createComponent("Ofono.qml"); |
100 | + modemsSorted.forEach(function (path, index) { |
101 | + var sim = component.createObject(root, { |
102 | + path: path, |
103 | + name: phoneSettings.simNames[path] ? |
104 | + phoneSettings.simNames[path] : |
105 | + "SIM " + (index + 1) |
106 | + }); |
107 | + if (sim === null) |
108 | + console.warn('failed to create sim object'); |
109 | + else |
110 | + Sims.add(sim); |
111 | + }); |
112 | + root.sims = Sims.getAll(); |
113 | + root.simsPresent = Sims.getPresentCount(); |
114 | + } |
115 | + } |
116 | + |
117 | + GSettings { |
118 | + id: phoneSettings |
119 | + schema.id: "com.ubuntu.phone" |
120 | + } |
121 | + |
122 | GSettings { |
123 | id: unitySettings |
124 | schema.id: "com.canonical.Unity.Lenses" |
125 | @@ -113,12 +154,19 @@ |
126 | } |
127 | ListItem.SingleValue { |
128 | id: simControl |
129 | + objectName: "simControl" |
130 | text: i18n.tr("SIM PIN") |
131 | - value: "Off" |
132 | + value: { |
133 | + if (simsLoaded === 1 && simsLocked > 0) |
134 | + return i18n.tr("On"); |
135 | + else if (simsLoaded > 1 && simsLocked > 0) |
136 | + return simsLocked + "/" + simsLoaded; |
137 | + else |
138 | + return i18n.tr("Off"); |
139 | + } |
140 | + visible: simsPresent > 0 |
141 | progression: true |
142 | - visible: showAllUI |
143 | - /* Not implemented yet */ |
144 | - //onClicked: pageStack.push(Qt.resolvedUrl("SimPin.qml")) |
145 | + onClicked: pageStack.push(Qt.resolvedUrl("SimPin.qml"), { sims: sims }) |
146 | } |
147 | ListItem.Standard { |
148 | text: i18n.tr("Encryption") |
149 | |
150 | === added file 'plugins/security-privacy/SimPin.qml' |
151 | --- plugins/security-privacy/SimPin.qml 1970-01-01 00:00:00 +0000 |
152 | +++ plugins/security-privacy/SimPin.qml 2014-08-27 21:27:21 +0000 |
153 | @@ -0,0 +1,358 @@ |
154 | +/* |
155 | + * Copyright (C) 2014 Canonical Ltd. |
156 | + * |
157 | + * This program is free software: you can redistribute it and/or modify it |
158 | + * under the terms of the GNU General Public License version 3, as published |
159 | + * by the Free Software Foundation. |
160 | + * |
161 | + * This program is distributed in the hope that it will be useful, but |
162 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
163 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
164 | + * PURPOSE. See the GNU General Public License for more details. |
165 | + * |
166 | + * You should have received a copy of the GNU General Public License along |
167 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
168 | + * |
169 | + * Authors: |
170 | + * Ken VanDine <ken.vandine@canonical.com> |
171 | + * |
172 | + */ |
173 | + |
174 | +import GSettings 1.0 |
175 | +import QtQuick 2.0 |
176 | +import QtQuick.Layouts 1.1 |
177 | +import Ubuntu.Components 1.1 |
178 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
179 | +import Ubuntu.Components.Popups 0.1 |
180 | +import SystemSettings 1.0 |
181 | +import MeeGo.QOfono 0.2 |
182 | +import "sims.js" as Sims |
183 | + |
184 | + |
185 | +ItemPage { |
186 | + id: root |
187 | + title: i18n.tr("SIM PIN") |
188 | + |
189 | + property var sims |
190 | + property var curSim |
191 | + |
192 | + Component { |
193 | + id: dialogComponent |
194 | + |
195 | + Dialog { |
196 | + id: changePinDialog |
197 | + title: i18n.tr("Change SIM PIN") |
198 | + |
199 | + property string errorText: i18n.tr( |
200 | + "Incorrect PIN. %1 attempts remaining." |
201 | + ).arg(curSim.pinRetries[OfonoSimManager.SimPin] || 3) |
202 | + property int simMin: curSim.minimumPinLength(OfonoSimManager.SimPin) |
203 | + property int simMax: curSim.maximumPinLength(OfonoSimManager.SimPin) |
204 | + |
205 | + // This is a bit hacky, but the contents of this dialog get so tall |
206 | + // that on a mako device, they don't fit with the OSK also visible. |
207 | + // So we scrunch up spacing. |
208 | + Binding { |
209 | + target: __foreground |
210 | + property: "itemSpacing" |
211 | + value: units.gu(1) |
212 | + } |
213 | + |
214 | + Connections { |
215 | + target: curSim |
216 | + onChangePinComplete: { |
217 | + if (error === OfonoSimManager.FailedError) { |
218 | + console.warn("Change PIN failed with: " + error); |
219 | + incorrect.visible = true; |
220 | + changePinDialog.enabled = true; |
221 | + currentInput.forceActiveFocus(); |
222 | + currentInput.selectAll(); |
223 | + return; |
224 | + } |
225 | + incorrect.visible = false; |
226 | + changePinDialog.enabled = true; |
227 | + PopupUtils.close(changePinDialog); |
228 | + } |
229 | + } |
230 | + |
231 | + Label { |
232 | + text: i18n.tr("Current PIN:") |
233 | + } |
234 | + |
235 | + TextField { |
236 | + id: currentInput |
237 | + echoMode: TextInput.Password |
238 | + inputMethodHints: Qt.ImhDialableCharactersOnly |
239 | + maximumLength: simMax |
240 | + } |
241 | + |
242 | + Label { |
243 | + id: retries |
244 | + text: i18n.tr("%1 attempts remaining").arg( |
245 | + curSim.pinRetries[OfonoSimManager.SimPin] || 3) |
246 | + visible: !incorrect.visible |
247 | + } |
248 | + |
249 | + Label { |
250 | + id: incorrect |
251 | + text: errorText |
252 | + visible: false |
253 | + color: "darkred" |
254 | + } |
255 | + |
256 | + Label { |
257 | + text: i18n.tr("Choose new PIN:") |
258 | + } |
259 | + |
260 | + TextField { |
261 | + id: newInput |
262 | + echoMode: TextInput.Password |
263 | + inputMethodHints: Qt.ImhDialableCharactersOnly |
264 | + maximumLength: simMax |
265 | + } |
266 | + |
267 | + Label { |
268 | + text: i18n.tr("Confirm new PIN:") |
269 | + } |
270 | + |
271 | + TextField { |
272 | + id: confirmInput |
273 | + echoMode: TextInput.Password |
274 | + inputMethodHints: Qt.ImhDialableCharactersOnly |
275 | + maximumLength: simMax |
276 | + |
277 | + // Doesn't get updated if you set this in enabled of confirmButton |
278 | + onTextChanged: confirmButton.enabled = |
279 | + (acceptableInput && |
280 | + text.length >= simMin && |
281 | + (text === newInput.text)) |
282 | + } |
283 | + |
284 | + Label { |
285 | + id: notMatching |
286 | + wrapMode: Text.Wrap |
287 | + text: i18n.tr("PINs don't match. Try again.") |
288 | + visible: false |
289 | + color: "darkred" |
290 | + } |
291 | + |
292 | + RowLayout { |
293 | + spacing: units.gu(1) |
294 | + |
295 | + Button { |
296 | + Layout.fillWidth: true |
297 | + color: UbuntuColors.lightGrey |
298 | + text: i18n.tr("Cancel") |
299 | + onClicked: PopupUtils.close(changePinDialog) |
300 | + } |
301 | + |
302 | + Button { |
303 | + id: confirmButton |
304 | + Layout.fillWidth: true |
305 | + color: UbuntuColors.green |
306 | + text: i18n.tr("Change") |
307 | + enabled: false |
308 | + onClicked: { |
309 | + changePinDialog.enabled = false |
310 | + var match = (newInput.text === confirmInput.text) |
311 | + notMatching.visible = !match |
312 | + if (!match) { |
313 | + changePinDialog.enabled = true |
314 | + newInput.forceActiveFocus() |
315 | + newInput.selectAll() |
316 | + return |
317 | + } |
318 | + curSim.changePin(OfonoSimManager.SimPin, currentInput.text, newInput.text); |
319 | + } |
320 | + } |
321 | + } |
322 | + } |
323 | + } |
324 | + |
325 | + Component { |
326 | + id: lockDialogComponent |
327 | + |
328 | + Dialog { |
329 | + id: lockPinDialog |
330 | + objectName: "lockDialogComponent" |
331 | + title: curSim.lockedPins.length > 0 ? |
332 | + i18n.tr("Enter SIM PIN") : |
333 | + i18n.tr("Enter Previous SIM PIN") |
334 | + |
335 | + property string errorText: i18n.tr( |
336 | + "Incorrect PIN. %1 attempts remaining." |
337 | + ).arg(curSim.pinRetries[OfonoSimManager.SimPin] || 3) |
338 | + |
339 | + property int simMin: curSim.minimumPinLength(OfonoSimManager.SimPin) |
340 | + property int simMax: curSim.maximumPinLength(OfonoSimManager.SimPin) |
341 | + |
342 | + // This is a bit hacky, but the contents of this dialog get so tall |
343 | + // that on a mako device, they don't fit with the OSK also visible. |
344 | + // So we scrunch up spacing. |
345 | + Binding { |
346 | + target: __foreground |
347 | + property: "itemSpacing" |
348 | + value: units.gu(1) |
349 | + } |
350 | + |
351 | + Connections { |
352 | + target: curSim |
353 | + onLockPinComplete: { |
354 | + if (error === OfonoSimManager.FailedError) { |
355 | + console.warn("Lock PIN failed with: " + error); |
356 | + incorrect.visible = true; |
357 | + lockPinDialog.enabled = true; |
358 | + prevInput.forceActiveFocus(); |
359 | + prevInput.selectAll(); |
360 | + return; |
361 | + } |
362 | + incorrect.visible = false; |
363 | + lockPinDialog.enabled = true; |
364 | + PopupUtils.close(lockPinDialog); |
365 | + } |
366 | + onUnlockPinComplete: { |
367 | + if (error === OfonoSimManager.FailedError) { |
368 | + console.warn("Unlock PIN failed with: " + error); |
369 | + incorrect.visible = true; |
370 | + lockPinDialog.enabled = true; |
371 | + prevInput.forceActiveFocus(); |
372 | + prevInput.selectAll(); |
373 | + return; |
374 | + } |
375 | + incorrect.visible = false; |
376 | + lockPinDialog.enabled = true; |
377 | + PopupUtils.close(lockPinDialog); |
378 | + } |
379 | + } |
380 | + |
381 | + TextField { |
382 | + id: prevInput |
383 | + objectName: "prevInput" |
384 | + echoMode: TextInput.Password |
385 | + inputMethodHints: Qt.ImhDialableCharactersOnly |
386 | + maximumLength: simMax |
387 | + |
388 | + // Doesn't get updated if you set this in enabled of confirmButton |
389 | + onTextChanged: lockButton.enabled = |
390 | + (acceptableInput && text.length >= simMin) |
391 | + } |
392 | + |
393 | + Label { |
394 | + text: i18n.tr("%1 attempts remaining").arg( |
395 | + curSim.pinRetries[OfonoSimManager.SimPin] || 3) |
396 | + visible: !incorrect.visible |
397 | + } |
398 | + |
399 | + Label { |
400 | + id: incorrect |
401 | + text: errorText |
402 | + visible: false |
403 | + color: "darkred" |
404 | + } |
405 | + |
406 | + RowLayout { |
407 | + spacing: units.gu(1) |
408 | + |
409 | + Button { |
410 | + objectName: "cancelButton" |
411 | + Layout.fillWidth: true |
412 | + color: UbuntuColors.lightGrey |
413 | + text: i18n.tr("Cancel") |
414 | + onClicked: { |
415 | + if (curSim.lockedPins.length < 1) |
416 | + caller.checked = false; |
417 | + else |
418 | + caller.checked = true; |
419 | + PopupUtils.close(lockPinDialog); |
420 | + } |
421 | + } |
422 | + |
423 | + Button { |
424 | + id: lockButton |
425 | + objectName: "lockButton" |
426 | + Layout.fillWidth: true |
427 | + color: UbuntuColors.green |
428 | + |
429 | + text: curSim.lockedPins.length > 0 ? i18n.tr("Unlock") : i18n.tr("Lock") |
430 | + enabled: false |
431 | + onClicked: { |
432 | + lockPinDialog.enabled = false; |
433 | + if (curSim.lockedPins.length > 0) |
434 | + curSim.unlockPin(OfonoSimManager.SimPin, prevInput.text); |
435 | + else |
436 | + curSim.lockPin(OfonoSimManager.SimPin, prevInput.text); |
437 | + } |
438 | + } |
439 | + } |
440 | + } |
441 | + } |
442 | + |
443 | + Column { |
444 | + anchors.left: parent.left |
445 | + anchors.right: parent.right |
446 | + |
447 | + Repeater { |
448 | + model: sims.length |
449 | + Column { |
450 | + anchors { |
451 | + left: parent.left |
452 | + right: parent.right |
453 | + } |
454 | + |
455 | + Connections { |
456 | + target: sims[index].simMng |
457 | + onLockedPinsChanged: { |
458 | + simPinSwitch.checked = |
459 | + sims[index].simMng.lockedPins.length > 0; |
460 | + } |
461 | + } |
462 | + |
463 | + ListItem.Standard { |
464 | + text: i18n.tr("%1").arg(sims[index].title) |
465 | + visible: sims.length > 1 |
466 | + } |
467 | + |
468 | + ListItem.Standard { |
469 | + text: i18n.tr("SIM PIN") |
470 | + control: Switch { |
471 | + id: simPinSwitch |
472 | + objectName: "simPinSwitch" |
473 | + checked: sims[index].simMng.lockedPins.length > 0 |
474 | + onClicked: { |
475 | + curSim = sims[index].simMng; |
476 | + PopupUtils.open(lockDialogComponent, simPinSwitch); |
477 | + } |
478 | + } |
479 | + showDivider: index < (sims.length - 1) && simPinSwitch.checked |
480 | + } |
481 | + |
482 | + ListItem.SingleControl { |
483 | + id: changeControl |
484 | + visible: sims[index].simMng.lockedPins.length > 0 |
485 | + control: Button { |
486 | + enabled: parent.visible |
487 | + text: i18n.tr("Change PIN…") |
488 | + width: parent.width - units.gu(4) |
489 | + onClicked: { |
490 | + curSim = sims[index].simMng; |
491 | + PopupUtils.open(dialogComponent); |
492 | + } |
493 | + } |
494 | + showDivider: false |
495 | + } |
496 | + ListItem.Divider { |
497 | + visible: index < (sims.length - 1) |
498 | + } |
499 | + |
500 | + } |
501 | + } |
502 | + |
503 | + ListItem.Caption { |
504 | + text: i18n.tr("When a SIM PIN is set, it must be entered to access cellular services after restarting the phone or swapping the SIM.") |
505 | + } |
506 | + |
507 | + ListItem.Caption { |
508 | + text: i18n.tr("Entering an incorrect PIN repeatedly may lock the SIM permanently.") |
509 | + } |
510 | + } |
511 | +} |
512 | |
513 | === added file 'plugins/security-privacy/sims.js' |
514 | --- plugins/security-privacy/sims.js 1970-01-01 00:00:00 +0000 |
515 | +++ plugins/security-privacy/sims.js 2014-08-27 21:27:21 +0000 |
516 | @@ -0,0 +1,34 @@ |
517 | +var sims = []; |
518 | + |
519 | +function add (sim) { |
520 | + sims.push(sim); |
521 | + root.simsLoaded++; |
522 | +} |
523 | + |
524 | +function getAll () { |
525 | + return sims; |
526 | +} |
527 | + |
528 | +function get (n) { |
529 | + return getAll()[n]; |
530 | +} |
531 | + |
532 | +function getCount () { |
533 | + return getAll().length; |
534 | +} |
535 | + |
536 | +function getPresent () { |
537 | + var present = []; |
538 | + getAll().forEach(function (sim) { |
539 | + if (sim.present) { |
540 | + present.push(sim); |
541 | + } else { |
542 | + return; |
543 | + } |
544 | + }); |
545 | + return present; |
546 | +} |
547 | + |
548 | +function getPresentCount () { |
549 | + return getPresent().length; |
550 | +} |
551 | |
552 | === modified file 'src/qml/CategoryGrid.qml' |
553 | --- src/qml/CategoryGrid.qml 2014-08-05 13:27:35 +0000 |
554 | +++ src/qml/CategoryGrid.qml 2014-08-27 21:27:21 +0000 |
555 | @@ -59,6 +59,7 @@ |
556 | pageStack.push(model.item.pageComponent, |
557 | { plugin: model.item, pluginManager: pluginManager }) |
558 | } |
559 | + loader.item.highlighted = false; |
560 | } |
561 | } |
562 | } |
563 | |
564 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py' |
565 | --- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-08-26 15:48:32 +0000 |
566 | +++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-08-27 21:27:21 +0000 |
567 | @@ -191,23 +191,6 @@ |
568 | "PropertyChanged", "sv", [args[0], args[1]])' |
569 | .replace('IFACE', RDO_IFACE)), ]) |
570 | |
571 | - def mock_sim_manager(self, modem, properties=None): |
572 | - if not properties: |
573 | - properties = { |
574 | - 'SubscriberNumbers': ['123456', '234567'] |
575 | - } |
576 | - modem.AddProperties(SIM_IFACE, properties) |
577 | - modem.AddProperty(SIM_IFACE, 'Present', True) |
578 | - modem.AddMethods( |
579 | - SIM_IFACE, |
580 | - [('GetProperties', '', 'a{sv}', |
581 | - 'ret = self.GetAll("%s")' % SIM_IFACE), |
582 | - ('SetProperty', 'sv', '', |
583 | - 'self.Set("IFACE", args[0], args[1]); ' |
584 | - 'self.EmitSignal("IFACE",\ |
585 | - "PropertyChanged", "sv", [args[0], args[1]])' |
586 | - .replace('IFACE', SIM_IFACE)), ]) |
587 | - |
588 | def mock_call_forwarding(self, modem): |
589 | modem.AddProperty( |
590 | CALL_FWD_IFACE, 'VoiceUnconditional', '') |
591 | @@ -241,7 +224,6 @@ |
592 | self.mock_carriers('ril_0') |
593 | self.mock_radio_settings(self.modem_0) |
594 | self.mock_connection_manager(self.modem_0) |
595 | - self.mock_sim_manager(self.modem_0) |
596 | self.mock_call_forwarding(self.modem_0) |
597 | self.mock_call_settings(self.modem_0) |
598 | |
599 | @@ -276,9 +258,10 @@ |
600 | self.mock_call_forwarding(self.modem_1) |
601 | self.mock_call_settings(self.modem_1) |
602 | |
603 | - self.mock_sim_manager(self.modem_1, { |
604 | - 'SubscriberNumbers': ['08123', '938762783'] |
605 | - }) |
606 | + self.modem_1.Set( |
607 | + SIM_IFACE, |
608 | + 'SubscriberNumbers', ['08123', '938762783'] |
609 | + ) |
610 | |
611 | @classmethod |
612 | def setUpClass(cls): |
613 | @@ -679,7 +662,7 @@ |
614 | super(ResetBaseTestCase, self).tearDown() |
615 | |
616 | |
617 | -class SecurityBaseTestCase(UbuntuSystemSettingsTestCase): |
618 | +class SecurityBaseTestCase(UbuntuSystemSettingsOfonoTestCase): |
619 | """ Base class for security and privacy settings tests""" |
620 | |
621 | def setUp(self): |
622 | |
623 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/ofono.py' |
624 | --- tests/autopilot/ubuntu_system_settings/tests/ofono.py 2014-08-19 12:35:00 +0000 |
625 | +++ tests/autopilot/ubuntu_system_settings/tests/ofono.py 2014-08-27 21:27:21 +0000 |
626 | @@ -81,6 +81,7 @@ |
627 | ]) |
628 | obj = dbusmock.mockobject.objects[path] |
629 | obj.name = name |
630 | + add_simmanager_api(obj) |
631 | add_voice_call_api(obj) |
632 | add_netreg_api(obj) |
633 | self.modems.append(path) |
634 | @@ -89,6 +90,39 @@ |
635 | return path |
636 | |
637 | |
638 | +def add_simmanager_api(mock): |
639 | + '''Add org.ofono.SimManager API to a mock''' |
640 | + |
641 | + iface = 'org.ofono.SimManager' |
642 | + mock.AddProperties(iface, { |
643 | + 'CardIdentifier': _parameters.get('CardIdentifier', 12345), |
644 | + 'Present': _parameters.get('Present', dbus.Boolean(True)), |
645 | + 'SubscriberNumbers': _parameters.get('SubscriberNumbers', |
646 | + ['123456789', '234567890']), |
647 | + 'SubscriberIdentity': _parameters.get('SubscriberIdentity', 23456), |
648 | + 'LockedPins': _parameters.get('LockedPins', ['pin']), |
649 | + 'Retries': _parameters.get('Retries', {'pin': dbus.Byte(3)}), |
650 | + 'PinRequired': _parameters.get('PinRequired', 'none') |
651 | + }) |
652 | + |
653 | + mock.AddMethods(iface, [ |
654 | + ('GetProperties', '', 'a{sv}', 'ret = self.GetAll("%s")' % iface), |
655 | + ('SetProperty', 'sv', '', 'self.Set("%(i)s", args[0], args[1]); ' |
656 | + 'self.EmitSignal("%(i)s", "PropertyChanged", "sv", [args[0], ' |
657 | + 'args[1]])' % {'i': iface}), |
658 | + ('ChangePin', 'sss', '', ''), |
659 | + ('EnterPin', 'ss', '', ''), |
660 | + ('ResetPin', 'sss', '', ''), |
661 | + ('LockPin', 'ss', '', 'if args[1] == "2468": self.Set("%(i)s",' |
662 | + '"LockedPins", dbus.Array(["pin"])); self.EmitSignal("%(i)s",' |
663 | + '"PropertyChanged", "sv", ["LockedPins", self.Get("%(i)s", ' |
664 | + '"LockedPins")])' % {'i': iface}), |
665 | + ('UnlockPin', 'ss', '', 'if args[1] == "2468": self.Set("%(i)s",' |
666 | + '"LockedPins", ""); self.EmitSignal("%(i)s", "PropertyChanged", "sv",' |
667 | + ' ["LockedPins", self.Get("%(i)s", "LockedPins")])' % {'i': iface}) |
668 | + ]) |
669 | + |
670 | + |
671 | def add_voice_call_api(mock): |
672 | '''Add org.ofono.VoiceCallManager API to a mock''' |
673 | |
674 | |
675 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/test_security.py' |
676 | --- tests/autopilot/ubuntu_system_settings/tests/test_security.py 2014-08-21 21:08:31 +0000 |
677 | +++ tests/autopilot/ubuntu_system_settings/tests/test_security.py 2014-08-27 21:27:21 +0000 |
678 | @@ -7,10 +7,13 @@ |
679 | |
680 | from gi.repository import Gio |
681 | from time import sleep |
682 | +import unittest |
683 | from testtools.matchers import Equals, NotEquals |
684 | from autopilot.matchers import Eventually |
685 | |
686 | -from ubuntu_system_settings.tests import SecurityBaseTestCase |
687 | +from ubuntu_system_settings.tests import ( |
688 | + SecurityBaseTestCase, |
689 | + SIM_IFACE) |
690 | |
691 | from ubuntu_system_settings.utils.i18n import ugettext as _ |
692 | from ubuntuuitoolkit import emulators as toolkit_emulators |
693 | @@ -21,6 +24,7 @@ |
694 | |
695 | def setUp(self): |
696 | super(SecurityTestCase, self).setUp() |
697 | + self.assertEqual(['pin'], self.modem_0.Get(SIM_IFACE, 'LockedPins')) |
698 | prps = self.system_settings.main_view.security_page.get_properties() |
699 | self.use_powerd = prps['usePowerd'] |
700 | if self.use_powerd: |
701 | @@ -64,6 +68,12 @@ |
702 | ) |
703 | self.system_settings.main_view.scroll_to_and_click(selector) |
704 | |
705 | + def _go_to_sim_lock(self): |
706 | + selector = self.security_page.select_single( |
707 | + objectName="simControl" |
708 | + ) |
709 | + self.system_settings.main_view.scroll_to_and_click(selector) |
710 | + |
711 | def _go_to_sleep_values(self): |
712 | self._go_to_phone_lock() |
713 | selector = self.system_settings.main_view.select_single( |
714 | @@ -171,3 +181,210 @@ |
715 | selected_delegate = selector.select_single( |
716 | 'OptionSelectorDelegate', selected=True) |
717 | self.assertEquals(selected_delegate.text, 'After 4 minutes') |
718 | + |
719 | + def test_sim_pin_control_value(self): |
720 | + self.assertEqual('none', self.modem_0.Get(SIM_IFACE, 'PinRequired')) |
721 | + self.assertEqual(['pin'], self.modem_0.Get(SIM_IFACE, 'LockedPins')) |
722 | + |
723 | + sim_pin_value = self.security_page.select_single( |
724 | + objectName='simControl').value |
725 | + |
726 | + self.assertThat( |
727 | + sim_pin_value, |
728 | + Equals(_('On')) |
729 | + ) |
730 | + |
731 | + def test_sim_pin_lock_control(self): |
732 | + self._go_to_sim_lock() |
733 | + sim_lock_control = self.system_settings.main_view.select_single( |
734 | + objectName='simPinSwitch') |
735 | + locked = len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
736 | + self.assertEquals(locked, sim_lock_control.checked) |
737 | + |
738 | + def test_sim_pin_lock_control_unlock(self): |
739 | + self._go_to_sim_lock() |
740 | + sim_lock_control = self.system_settings.main_view.select_single( |
741 | + objectName='simPinSwitch') |
742 | + |
743 | + self.assertTrue(sim_lock_control.checked) |
744 | + |
745 | + self.system_settings.main_view.scroll_to_and_click(sim_lock_control) |
746 | + |
747 | + lock_dialog = self.system_settings.main_view.select_single( |
748 | + objectName='lockDialogComponent') |
749 | + self.assertEqual( |
750 | + lock_dialog.title, |
751 | + _("Enter SIM PIN") |
752 | + ) |
753 | + |
754 | + prev_input = self.system_settings.main_view.select_single( |
755 | + objectName='prevInput') |
756 | + submit_button = self.system_settings.main_view.select_single( |
757 | + objectName='lockButton') |
758 | + |
759 | + self.assertEqual( |
760 | + submit_button.text, |
761 | + _("Unlock") |
762 | + ) |
763 | + |
764 | + self.assertFalse( |
765 | + submit_button.get_properties()['enabled'] |
766 | + ) |
767 | + self.system_settings.main_view.scroll_to_and_click(prev_input) |
768 | + self.keyboard.type("246") |
769 | + self.assertFalse( |
770 | + submit_button.get_properties()['enabled'] |
771 | + ) |
772 | + self.keyboard.type("8") |
773 | + |
774 | + self.assertTrue( |
775 | + submit_button.get_properties()['enabled'] |
776 | + ) |
777 | + |
778 | + self.system_settings.main_view.scroll_to_and_click(submit_button) |
779 | + |
780 | + self.assertFalse(sim_lock_control.checked) |
781 | + |
782 | + locked = len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
783 | + self.assertEquals(locked, sim_lock_control.checked) |
784 | + |
785 | + @unittest.skip('skipped because the simPinSwitch state fails to update') |
786 | + def test_sim_pin_lock_control_lock(self): |
787 | + self.modem_0.Set(SIM_IFACE, 'LockedPins', "") |
788 | + self._go_to_sim_lock() |
789 | + sim_lock_control = self.system_settings.main_view.select_single( |
790 | + objectName='simPinSwitch') |
791 | + |
792 | + self.assertFalse(sim_lock_control.checked) |
793 | + |
794 | + self.system_settings.main_view.scroll_to_and_click(sim_lock_control) |
795 | + |
796 | + lock_dialog = self.system_settings.main_view.select_single( |
797 | + objectName='lockDialogComponent') |
798 | + self.assertEqual( |
799 | + lock_dialog.title, |
800 | + _("Enter SIM PIN") |
801 | + ) |
802 | + |
803 | + prev_input = self.system_settings.main_view.select_single( |
804 | + objectName='prevInput') |
805 | + submit_button = self.system_settings.main_view.select_single( |
806 | + objectName='lockButton') |
807 | + |
808 | + self.assertEqual( |
809 | + submit_button.text, |
810 | + _("Lock") |
811 | + ) |
812 | + |
813 | + self.assertFalse( |
814 | + submit_button.get_properties()['enabled'] |
815 | + ) |
816 | + self.system_settings.main_view.scroll_to_and_click(prev_input) |
817 | + self.keyboard.type("246") |
818 | + self.assertFalse( |
819 | + submit_button.get_properties()['enabled'] |
820 | + ) |
821 | + self.keyboard.type("8") |
822 | + |
823 | + self.assertTrue( |
824 | + submit_button.get_properties()['enabled'] |
825 | + ) |
826 | + |
827 | + self.system_settings.main_view.scroll_to_and_click(submit_button) |
828 | + |
829 | + self.assertTrue(sim_lock_control.checked) |
830 | + |
831 | + self.assertEqual(['pin'], self.modem_0.Get(SIM_IFACE, 'LockedPins')) |
832 | + locked = len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
833 | + self.assertEquals(locked, sim_lock_control.checked) |
834 | + |
835 | + def test_sim_pin_lock_control_unlock_fail(self): |
836 | + self._go_to_sim_lock() |
837 | + sim_lock_control = self.system_settings.main_view.select_single( |
838 | + objectName='simPinSwitch') |
839 | + |
840 | + self.assertTrue( |
841 | + len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
842 | + ) |
843 | + self.assertTrue(sim_lock_control.checked) |
844 | + |
845 | + self.system_settings.main_view.scroll_to_and_click(sim_lock_control) |
846 | + |
847 | + lock_dialog = self.system_settings.main_view.select_single( |
848 | + objectName='lockDialogComponent') |
849 | + self.assertEqual( |
850 | + lock_dialog.title, |
851 | + _("Enter SIM PIN") |
852 | + ) |
853 | + |
854 | + prev_input = self.system_settings.main_view.select_single( |
855 | + objectName='prevInput') |
856 | + submit_button = self.system_settings.main_view.select_single( |
857 | + objectName='lockButton') |
858 | + |
859 | + self.assertEqual( |
860 | + submit_button.text, |
861 | + _("Unlock") |
862 | + ) |
863 | + |
864 | + self.assertFalse( |
865 | + submit_button.get_properties()['enabled'] |
866 | + ) |
867 | + self.system_settings.main_view.scroll_to_and_click(prev_input) |
868 | + self.keyboard.type("1234") |
869 | + |
870 | + self.assertTrue( |
871 | + submit_button.get_properties()['enabled'] |
872 | + ) |
873 | + |
874 | + self.system_settings.main_view.scroll_to_and_click(submit_button) |
875 | + |
876 | + self.assertTrue( |
877 | + len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
878 | + ) |
879 | + |
880 | + @unittest.skip('skipped because the simPinSwitch state fails to update') |
881 | + def test_sim_pin_lock_control_lock_fail(self): |
882 | + self.modem_0.Set(SIM_IFACE, 'LockedPins', "") |
883 | + self._go_to_sim_lock() |
884 | + sim_lock_control = self.system_settings.main_view.select_single( |
885 | + objectName='simPinSwitch') |
886 | + |
887 | + self.assertFalse( |
888 | + len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
889 | + ) |
890 | + |
891 | + self.system_settings.main_view.scroll_to_and_click(sim_lock_control) |
892 | + |
893 | + lock_dialog = self.system_settings.main_view.select_single( |
894 | + objectName='lockDialogComponent') |
895 | + self.assertEqual( |
896 | + lock_dialog.title, |
897 | + _("Enter SIM PIN") |
898 | + ) |
899 | + |
900 | + prev_input = self.system_settings.main_view.select_single( |
901 | + objectName='prevInput') |
902 | + submit_button = self.system_settings.main_view.select_single( |
903 | + objectName='lockButton') |
904 | + |
905 | + self.assertEqual( |
906 | + submit_button.text, |
907 | + _("Lock") |
908 | + ) |
909 | + |
910 | + self.assertFalse( |
911 | + submit_button.get_properties()['enabled'] |
912 | + ) |
913 | + self.system_settings.main_view.scroll_to_and_click(prev_input) |
914 | + self.keyboard.type("1234") |
915 | + |
916 | + self.assertTrue( |
917 | + submit_button.get_properties()['enabled'] |
918 | + ) |
919 | + |
920 | + self.system_settings.main_view.scroll_to_and_click(submit_button) |
921 | + |
922 | + self.assertFalse( |
923 | + len(self.modem_0.Get(SIM_IFACE, 'LockedPins')) > 0 |
924 | + ) |
PASSED: Continuous integration, rev:929 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- ci/1313/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/4134 jenkins. qa.ubuntu. com/job/ generic- mediumtests- utopic/ 3146 jenkins. qa.ubuntu. com/job/ ubuntu- system- settings- utopic- i386-ci/ 505 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- mako/3964 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/5386 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/5386/ artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 12225 jenkins. qa.ubuntu. com/job/ autopilot- testrunner- otto-utopic/ 2571 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/3433 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/3433/ artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- system- settings- ci/1313/ rebuild
http://