Merge lp:~nick-dedekind/indicator-network/simunlock.dialog into lp:indicator-network/13.10
- simunlock.dialog
- Merge into trunk.13.10
Status: | Merged |
---|---|
Approved by: | Ted Gould |
Approved revision: | 291 |
Merged at revision: | 299 |
Proposed branch: | lp:~nick-dedekind/indicator-network/simunlock.dialog |
Merge into: | lp:indicator-network/13.10 |
Diff against target: |
1038 lines (+755/-47) 10 files modified
CMakeLists.txt (+7/-1) debian/control (+1/-0) network/CMakeLists.txt (+17/-0) network/device-mobile.vala (+118/-16) network/mobile-sim-manager.vala (+545/-0) network/network-action-manager.vala (+6/-24) network/network-menu-service.vala (+3/-2) network/network-menu.vala (+5/-4) network/ofono.vala (+30/-0) network/utils.vala (+23/-0) |
To merge this branch: | bzr merge lp:~nick-dedekind/indicator-network/simunlock.dialog |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ted Gould (community) | Approve | ||
Michał Sawicz | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Pete Woods | Pending | ||
Review via email: mp+185810@code.launchpad.net |
Commit message
SIM pin/puk unload menu & dialog notification.
Description of the change
SIM pin/puk unload menu & dialog notification.
Pete Woods (pete-woods) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:290
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Nick Dedekind (nick-dedekind) wrote : | # |
> I would really like to see some tests for this along the lines of what I did
> for the secret agent (https:/
> /indicator-
eh. This is so embedded with the mobile device now (ofono). Will need to extract all of that to enable testing. At this point I think we're beyond that. It's a blocker for v1.
2nd iteration?
Pete Woods (pete-woods) wrote : | # |
> > I would really like to see some tests for this along the lines of what I did
> > for the secret agent (https:/
> developers
> > /indicator-
>
> eh. This is so embedded with the mobile device now (ofono). Will need to
> extract all of that to enable testing. At this point I think we're beyond
> that. It's a blocker for v1.
> 2nd iteration?
That comment was quite some time ago. Feel free to hack away!
- 291. By Nick Dedekind
-
merged with trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:291
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : | # |
Yeah, this is working!
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-10-03 12:08:41 +0000 |
3 | +++ CMakeLists.txt 2013-10-10 15:57:20 +0000 |
4 | @@ -16,7 +16,7 @@ |
5 | include(UseGSettings) |
6 | |
7 | # Workaround for libexecdir on debian |
8 | -if (EXISTS "/etc/debian_version") |
9 | +if (EXISTS "/etc/debian_version") |
10 | set(CMAKE_INSTALL_LIBEXECDIR ${CMAKE_INSTALL_LIBDIR}) |
11 | set(CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}") |
12 | endif() |
13 | @@ -41,6 +41,12 @@ |
14 | ) |
15 | include_directories(${NM_INCLUDE_DIRS}) |
16 | |
17 | +set(NOTIFY_REQUIRED_VERSION 0.7.5) |
18 | +pkg_check_modules( |
19 | + NOTIFY REQUIRED |
20 | + libnotify>=${NOTIFY_REQUIRED_VERSION} |
21 | +) |
22 | +include_directories(${NOTIFY_INCLUDE_DIRS}) |
23 | |
24 | find_package(Qt5Core REQUIRED) |
25 | include_directories(${Qt5Core_INCLUDE_DIRS}) |
26 | |
27 | === modified file 'debian/control' |
28 | --- debian/control 2013-10-03 13:04:00 +0000 |
29 | +++ debian/control 2013-10-10 15:57:20 +0000 |
30 | @@ -10,6 +10,7 @@ |
31 | libglib2.0-dev, |
32 | libnm-glib-dev (>= 0.9), |
33 | libnm-util-dev (>= 0.9), |
34 | + libnotify-dev, |
35 | libqmenumodel-dev, |
36 | libqtdbusmock1-dev, |
37 | libqtdbustest1-dev, |
38 | |
39 | === modified file 'network/CMakeLists.txt' |
40 | --- network/CMakeLists.txt 2013-10-08 04:00:49 +0000 |
41 | +++ network/CMakeLists.txt 2013-10-10 15:57:20 +0000 |
42 | @@ -13,6 +13,7 @@ |
43 | vala_init(libnetwork |
44 | PACKAGES |
45 | libnm-glib |
46 | + libnotify |
47 | CUSTOM_VAPIS |
48 | "${CMAKE_CURRENT_SOURCE_DIR}/action-muxer.vapi" |
49 | "${CMAKE_CURRENT_SOURCE_DIR}/url-dispatcher.vapi" |
50 | @@ -24,9 +25,14 @@ |
51 | ) |
52 | |
53 | vala_add(libnetwork |
54 | + utils.vala |
55 | +) |
56 | + |
57 | +vala_add(libnetwork |
58 | network-action-manager.vala |
59 | DEPENDS |
60 | ofono |
61 | + utils |
62 | ) |
63 | |
64 | vala_add(libnetwork |
65 | @@ -40,6 +46,14 @@ |
66 | settings-wifi |
67 | settings-airplane |
68 | network-action-manager |
69 | + mobile-sim-manager |
70 | +) |
71 | + |
72 | +vala_add(libnetwork |
73 | + mobile-sim-manager.vala |
74 | + DEPENDS |
75 | + ofono |
76 | + utils |
77 | ) |
78 | |
79 | vala_add(libnetwork |
80 | @@ -56,6 +70,7 @@ |
81 | device-mobile.vala |
82 | DEPENDS |
83 | device-base |
84 | + mobile-sim-manager |
85 | ) |
86 | |
87 | vala_add(libnetwork |
88 | @@ -117,6 +132,7 @@ |
89 | network |
90 | ${GLIB_LIBRARIES} |
91 | ${NM_LIBRARIES} |
92 | + ${NOTIFY_LIBRARIES} |
93 | ) |
94 | |
95 | ########################### |
96 | @@ -126,6 +142,7 @@ |
97 | vala_init(network-service |
98 | PACKAGES |
99 | libnm-glib |
100 | + libnotify |
101 | libnetwork |
102 | DEPENDS |
103 | "${CMAKE_CURRENT_BINARY_DIR}/libnetwork.vapi" |
104 | |
105 | === modified file 'network/device-mobile.vala' |
106 | --- network/device-mobile.vala 2013-08-25 19:00:08 +0000 |
107 | +++ network/device-mobile.vala 2013-10-10 15:57:20 +0000 |
108 | @@ -22,11 +22,122 @@ |
109 | |
110 | namespace Network.Device |
111 | { |
112 | + internal class MobileMenu |
113 | + { |
114 | + private NM.Client client; |
115 | + private NM.DeviceModem device; |
116 | + private Menu apsmenu; |
117 | + private string action_prefix; |
118 | + private MobileSimManager mobilesimmanager; |
119 | + |
120 | + private MenuItem device_item; |
121 | + private MenuItem settings_item; |
122 | + private MenuItem? unlock_sim_item = null; |
123 | + |
124 | + public MobileMenu (NM.Client client, DeviceModem device, Menu global_menu, string action_prefix, bool show_enable, MobileSimManager mobilesimmanager) |
125 | + { |
126 | + this.client = client; |
127 | + this.device = device; |
128 | + this.apsmenu = global_menu; |
129 | + this.action_prefix = action_prefix; |
130 | + this.mobilesimmanager = mobilesimmanager; |
131 | + |
132 | + if (show_enable) { |
133 | + device_item = create_item_for_mobile_device(); |
134 | + this.apsmenu.append_item(device_item); |
135 | + } |
136 | + |
137 | + settings_item = new MenuItem(_("Cellular settings…"), "indicator.global.settings::cellular"); |
138 | + this.apsmenu.append_item(settings_item); |
139 | + |
140 | + update_sim_lock_menu(mobilesimmanager.pin_required); |
141 | + mobilesimmanager.notify["pin-required"].connect((s, value) => { |
142 | + update_sim_lock_menu(mobilesimmanager.pin_required); |
143 | + }); |
144 | + } |
145 | + |
146 | + ~MobileMenu () |
147 | + { |
148 | + } |
149 | + |
150 | + private MenuItem create_item_for_mobile_device () |
151 | + { |
152 | + var device_item = new MenuItem(_("Cellular"), action_prefix + ".device-enabled"); |
153 | + device_item.set_attribute ("x-canonical-type" , "s", "com.canonical.indicator.switch"); |
154 | + |
155 | + return device_item; |
156 | + } |
157 | + |
158 | + private void update_sim_lock_menu(bool pin_required) |
159 | + { |
160 | + string action_name = action_prefix + "unlock"; |
161 | + debug(@"sim lock updated $(pin_required) - action $action_name"); |
162 | + for (int i = 0; i < apsmenu.get_n_items(); i++) |
163 | + { |
164 | + string name; |
165 | + |
166 | + if (!apsmenu.get_item_attribute (i, "action", "s", out name)) |
167 | + continue; |
168 | + debug(@"update_sim_lock_menu action $name"); |
169 | + |
170 | + if (name == action_name) { |
171 | + if (!pin_required) { |
172 | + apsmenu.remove (i); |
173 | + } |
174 | + return; |
175 | + } |
176 | + } |
177 | + |
178 | + if (pin_required) { |
179 | + unlock_sim_item = new MenuItem(_("Unlock SIM…"), action_name); |
180 | + apsmenu.insert_item (0, unlock_sim_item); |
181 | + } else { |
182 | + unlock_sim_item = null; |
183 | + } |
184 | + } |
185 | + } |
186 | + |
187 | + internal class MobileActionManager |
188 | + { |
189 | + private SimpleActionGroup actions; |
190 | + private NM.Client client; |
191 | + private NM.DeviceModem device; |
192 | + private MobileSimManager mobilesimmanager; |
193 | + private SimpleAction unlock_action; |
194 | + |
195 | + public MobileActionManager (SimpleActionGroup actions, NM.Client client, NM.DeviceModem device, MobileSimManager mobilesimmanager) |
196 | + { |
197 | + this.actions = actions; |
198 | + this.client = client; |
199 | + this.device = device; |
200 | + this.mobilesimmanager = mobilesimmanager; |
201 | + |
202 | + unlock_action = new SimpleAction("unlock", null); |
203 | + unlock_action.activate.connect((ac,ps) => { |
204 | + if (mobilesimmanager.pin_unlocking) { |
205 | + debug(@"SIM unlock already in progress"); |
206 | + return; |
207 | + } |
208 | + mobilesimmanager.send_unlock_notification(); |
209 | + }); |
210 | + actions.insert(unlock_action); |
211 | + |
212 | + unlock_action.set_enabled(mobilesimmanager.pin_required && !mobilesimmanager.pin_unlocking); |
213 | + mobilesimmanager.notify["pin-required"].connect((s, value) => { |
214 | + unlock_action.set_enabled(mobilesimmanager.pin_required && !mobilesimmanager.pin_unlocking); |
215 | + }); |
216 | + mobilesimmanager.notify["pin-unlocking"].connect((s, value) => { |
217 | + unlock_action.set_enabled(mobilesimmanager.pin_required && !mobilesimmanager.pin_unlocking); |
218 | + }); |
219 | + } |
220 | + } |
221 | + |
222 | public class Mobile : Base { |
223 | - private GLib.MenuItem enabled_item; |
224 | - private GLib.MenuItem settings_item; |
225 | + private MobileMenu mobilemenu; |
226 | + private MobileActionManager mobileactionmanager; |
227 | + private MobileSimManager mobilesimmanager; |
228 | |
229 | - public Mobile (NM.Client client, NM.DeviceModem device, GLibLocal.ActionMuxer muxer, bool show_enable) { |
230 | + public Mobile (NM.Client client, NM.DeviceModem device, GLibLocal.ActionMuxer muxer, bool show_enable, GLib.DBusConnection conn) { |
231 | GLib.Object( |
232 | client: client, |
233 | device: device, |
234 | @@ -34,19 +145,9 @@ |
235 | muxer: muxer |
236 | ); |
237 | |
238 | - if (show_enable) { |
239 | - enabled_item = new MenuItem(_("Cellular"), "indicator." + device.get_iface() + ".device-enabled"); |
240 | - enabled_item.set_attribute ("x-canonical-type" , "s", "com.canonical.indicator.switch"); |
241 | - _menu.append_item(enabled_item); |
242 | - } |
243 | - |
244 | - settings_item = new MenuItem(_("Cellular settings…"), "indicator.global.settings::cellular"); |
245 | - _menu.append_item(settings_item); |
246 | - } |
247 | - |
248 | - ~Mobile () |
249 | - { |
250 | - muxer.remove(namespace); |
251 | + mobilesimmanager = new MobileSimManager(client, device, conn, this.namespace); |
252 | + mobilemenu = new MobileMenu(client, device, this._menu, "indicator." + this.namespace + ".", show_enable, mobilesimmanager); |
253 | + mobileactionmanager = new MobileActionManager(actions, client, device, mobilesimmanager); |
254 | } |
255 | |
256 | protected override void disable_device () |
257 | @@ -61,4 +162,5 @@ |
258 | device.set_autoconnect(true); |
259 | } |
260 | } |
261 | + |
262 | } |
263 | |
264 | === added file 'network/mobile-sim-manager.vala' |
265 | --- network/mobile-sim-manager.vala 1970-01-01 00:00:00 +0000 |
266 | +++ network/mobile-sim-manager.vala 2013-10-10 15:57:20 +0000 |
267 | @@ -0,0 +1,545 @@ |
268 | +// vim: tabstop=4 noexpandtab shiftwidth=4 softtabstop=4 |
269 | +/* |
270 | + * Copyright 2013 Canonical Ltd. |
271 | + * |
272 | + * This program is free software; you can redistribute it and/or modify |
273 | + * it under the terms of the GNU General Public License as published by |
274 | + * the Free Software Foundation; version 3. |
275 | + * |
276 | + * This program is distributed in the hope that it will be useful, |
277 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
278 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
279 | + * GNU Lesser General Public License for more details. |
280 | + * |
281 | + * You should have received a copy of the GNU Lesser General Public License |
282 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
283 | + * |
284 | + * Authors: |
285 | + * Nick Dedekind <nick-dedekind@canonical.com |
286 | + */ |
287 | + |
288 | +using NM; |
289 | +using Notify; |
290 | + |
291 | +namespace Network |
292 | +{ |
293 | + public class MobileSimManager : GLib.Object |
294 | + { |
295 | + public bool sim_installed { get; private set; default = false; } |
296 | + public bool pin_required { get { return required_pin != "none" && required_pin != ""; } } |
297 | + public bool pin_unlocking { get; private set; default = false; } |
298 | + |
299 | + private NM.Client client; |
300 | + private NM.DeviceModem device; |
301 | + private GLib.DBusConnection conn; |
302 | + private string namespace; |
303 | + private oFono.SIMManager? simmanager = null; |
304 | + private oFono.Modem? ofono_modem = null; |
305 | + private HashTable<string, uchar> retries = new HashTable<string, uchar>(str_hash, str_equal); |
306 | + private List<CurrentNotification?> notifications = new List<CurrentNotification?> (); |
307 | + |
308 | + // Sim Unlocking |
309 | + private string _required_pin = "none"; |
310 | + private string last_required_pin = ""; |
311 | + private string puk_code = ""; |
312 | + private string old_pin = ""; |
313 | + private string new_pin = ""; |
314 | + private int export_id = 0; |
315 | + |
316 | + private const string APPLICATION_ID = "com.canonical.indicator.network"; |
317 | + private const string SIM_UNLOCK_MENU_PATH = "/com/canonical/indicator/network/unlocksim"; |
318 | + private const string SIM_UNLOCK_ACTION_PATH = "/com/canonical/indicator/network/unlocksim"; |
319 | + private const string OFONO_ERROR_FAILED = "org.ofono.Error.Failed"; |
320 | + |
321 | + struct CurrentNotification { |
322 | + public int id; |
323 | + public Notification? notification; |
324 | + public uint unlock_menu_export_id; |
325 | + public uint unlock_actions_export_id; |
326 | + public Menu? unlock_menu; |
327 | + public SimpleActionGroup? unlock_actions; |
328 | + |
329 | + public CurrentNotification() { |
330 | + this.id = 0; |
331 | + this.notification = null; |
332 | + this.unlock_menu_export_id = 0; |
333 | + this.unlock_actions_export_id = 0; |
334 | + this.unlock_menu = null; |
335 | + this.unlock_actions = null; |
336 | + } |
337 | + } |
338 | + |
339 | + private string required_pin { |
340 | + get { return _required_pin; } |
341 | + set { if (_required_pin != value) { _required_pin = value; notify_property("pin-required"); } } |
342 | + } |
343 | + |
344 | + delegate void PinCancelCallback (); |
345 | + delegate void PinEnteredCallback (string pin); |
346 | + |
347 | + public MobileSimManager (NM.Client client, DeviceModem device, GLib.DBusConnection conn, string namespace) |
348 | + { |
349 | + this.client = client; |
350 | + this.device = device; |
351 | + this.conn = conn; |
352 | + this.namespace = namespace; |
353 | + |
354 | + create_ofono_sim_manager(); |
355 | + } |
356 | + |
357 | + public bool send_unlock_notification () |
358 | + { |
359 | + if (pin_required == false) { |
360 | + clear_stored_data(); |
361 | + return false; |
362 | + } |
363 | + |
364 | + if (send_unlock (required_pin == "puk" ? _("Please enter SIM PUK") : _("Please enter SIM PIN"), |
365 | + required_pin, |
366 | + required_pin == "puk" ? 8 : 4, |
367 | + pin_unlock_entered)) { |
368 | + debug(@"SIM Unlock: $required_pin"); |
369 | + last_required_pin = required_pin; |
370 | + pin_unlocking = true; |
371 | + debug(@"PIN LOCKING: $pin_unlocking"); |
372 | + return true; |
373 | + } else { |
374 | + debug(@"SIM unlock notification request failed"); |
375 | + clear_stored_data(); |
376 | + return false; |
377 | + } |
378 | + } |
379 | + |
380 | + public bool send_change_pin_notification() |
381 | + { |
382 | + if (send_unlock(_("Please enter SIM PIN"), "pin", 4, pin_change_old_entered)) { |
383 | + debug(@"SIM change. Sent old pin request"); |
384 | + return true; |
385 | + } else { |
386 | + debug(@"SIM change. Old pin notification failed"); |
387 | + return false; |
388 | + } |
389 | + } |
390 | + |
391 | + private bool send_unlock(string title, string? retry_type, int pin_length, PinEnteredCallback? pin_entered_callback) |
392 | + { |
393 | + string body = ""; |
394 | + if (retry_type != null) { |
395 | + uchar pin_retries = 0; |
396 | + if (retries.lookup_extended(retry_type, null, out pin_retries)) { |
397 | + if (pin_retries > 1) { |
398 | + body = _(@"$(pin_retries) attempts remaining"); |
399 | + } else if (pin_retries == 1) { |
400 | + body = _(@"$(pin_retries) attempt remaining"); |
401 | + } else { |
402 | + debug(@"No pin retries remaining"); |
403 | + send_fail_notification(_("No retries remaining")); |
404 | + return false; |
405 | + } |
406 | + } |
407 | + } |
408 | + |
409 | + // set the export hints |
410 | + string exported_action_path = @"$(SIM_UNLOCK_ACTION_PATH)$(export_id)"; |
411 | + string exported_menu_path = @"$(SIM_UNLOCK_MENU_PATH)$(export_id)"; |
412 | + export_id++; |
413 | + |
414 | + VariantBuilder menu_model_actions = new VariantBuilder (new VariantType ("a{sv}") ); |
415 | + menu_model_actions.add ("{sv}", "notifications", new Variant.string (exported_action_path)); |
416 | + |
417 | + VariantBuilder menu_model_paths = new VariantBuilder (new VariantType ("a{sv}") ); |
418 | + menu_model_paths.add ("{sv}", "busName", new Variant.string (APPLICATION_ID)); |
419 | + menu_model_paths.add ("{sv}", "menuPath", new Variant.string (exported_menu_path)); |
420 | + menu_model_paths.add ("{sv}", "actions", menu_model_actions.end ()); |
421 | + |
422 | + // create the menu |
423 | + var menu = new Menu (); |
424 | + var pin_unlock = new MenuItem ("", "notifications." + namespace + ".simunlock"); |
425 | + pin_unlock.set_attribute ("x-canonical-type", "s", "com.canonical.snapdecision.pinlock"); |
426 | + pin_unlock.set_attribute ("x-canonical-pin-length", "i", pin_length); |
427 | + menu.append_item (pin_unlock); |
428 | + |
429 | + // create the actions |
430 | + var actions = new SimpleActionGroup(); |
431 | + var unlockpin_item = new SimpleAction.stateful(namespace + ".simunlock", VariantType.BOOLEAN, new Variant.string("")); |
432 | + unlockpin_item.activate.connect ((ac, value) => { |
433 | + if (value.get_boolean() == false) { |
434 | + unlock_cancelled(); |
435 | + } |
436 | + }); |
437 | + if (pin_entered_callback != null) { |
438 | + unlockpin_item.change_state.connect ((ac, value) => { |
439 | + pin_entered_callback(value.get_string()); |
440 | + }); |
441 | + } |
442 | + actions.insert (unlockpin_item); |
443 | + |
444 | + // export the menu |
445 | + uint menu_export_id = 0; |
446 | + try { |
447 | + menu_export_id = conn.export_menu_model(exported_menu_path, menu); |
448 | + } catch (Error e) { |
449 | + warning(@"Unable to export sim unlock menu model for '$(device.get_iface())': $(e.message)"); |
450 | + clear_stored_data(); |
451 | + return false; |
452 | + } |
453 | + |
454 | + // export the actions |
455 | + uint actions_export_id = 0; |
456 | + try { |
457 | + actions_export_id = conn.export_action_group(exported_action_path, actions as ActionGroup); |
458 | + } catch (Error e) { |
459 | + warning(@"Unable to export sim unlock actions group for '$(device.get_iface())': $(e.message)"); |
460 | + if (menu_export_id != 0) { |
461 | + conn.unexport_menu_model(menu_export_id); |
462 | + } |
463 | + clear_stored_data(); |
464 | + return false; |
465 | + } |
466 | + |
467 | + // create and show the notification |
468 | + var notification = new Notification(title, body, ""); |
469 | + notification.set_hint_string ("x-canonical-snap-decisions", "true"); |
470 | + notification.set_hint ("x-canonical-private-menu-model", menu_model_paths.end ()); |
471 | + |
472 | + CurrentNotification new_notification = CurrentNotification() { |
473 | + notification = notification, |
474 | + unlock_menu_export_id = menu_export_id, |
475 | + unlock_actions_export_id = actions_export_id, |
476 | + unlock_menu = menu, |
477 | + unlock_actions = actions |
478 | + }; |
479 | + |
480 | + try { |
481 | + new_notification.notification.closed.connect (notification_closed); |
482 | + new_notification.notification.show (); |
483 | + |
484 | + notifications.append(new_notification); |
485 | + } catch (Error e) { |
486 | + warning(@"Unable to create sim unlock unlock notification for '$(device.get_iface())': $(e.message)"); |
487 | + clear_notification(new_notification); |
488 | + clear_stored_data(); |
489 | + return false; |
490 | + } |
491 | + return true; |
492 | + } |
493 | + |
494 | + private void send_fail_notification (string title) |
495 | + { |
496 | + try { |
497 | + var notification = new Notification(title, "", ""); |
498 | + notification.closed.connect (notification_closed); |
499 | + notification.show (); |
500 | + } catch (Error e) { |
501 | + warning(@"Unable to create sim unlock unlock notification for '$(device.get_iface())': $(e.message)"); |
502 | + return; |
503 | + } |
504 | + } |
505 | + |
506 | + private void create_ofono_sim_manager() |
507 | + { |
508 | + try { |
509 | + if (ofono_modem == null) { |
510 | + ofono_modem = Bus.get_proxy_sync (BusType.SYSTEM, "org.ofono", device.get_iface()); |
511 | + } |
512 | + |
513 | + ofono_modem.property_changed.connect((prop, value) => { |
514 | + if (prop == "Interfaces") { |
515 | + create_ofono_sim_manager(); |
516 | + } |
517 | + }); |
518 | + |
519 | + var modem_properties = ofono_modem.get_properties(); |
520 | + var interfaces = modem_properties.lookup("Interfaces"); |
521 | + |
522 | + if (interfaces == null) { |
523 | + debug(@"Modem '$(device.get_iface())' doesn't have voice support, no interfaces"); |
524 | + return; |
525 | + } |
526 | + |
527 | + if (!Utils.variant_contains(interfaces, "org.ofono.SimManager")) { |
528 | + debug(@"Modem '$(device.get_iface())' doesn't have SIM management support only: $(interfaces.print(false))"); |
529 | + return; |
530 | + } |
531 | + } catch (Error e) { |
532 | + warning(@"Unable to get oFono modem properties for '$(device.get_iface())': $(e.message)"); |
533 | + return; |
534 | + } |
535 | + |
536 | + try { |
537 | + /* Initialize the SIM Manager */ |
538 | + simmanager = Bus.get_proxy_sync (BusType.SYSTEM, "org.ofono", device.get_iface()); |
539 | + simmanager.property_changed.connect(simmanager_property); |
540 | + var simprops = simmanager.get_properties(); |
541 | + simprops.foreach((k, v) => { |
542 | + simmanager_property(k, v); |
543 | + }); |
544 | + |
545 | + } catch (Error e) { |
546 | + warning(@"Unable to get oFono information from $(device.get_iface()): $(e.message)"); |
547 | + simmanager = null; |
548 | + } |
549 | + |
550 | + return; |
551 | + } |
552 | + |
553 | + /* Properties from the SIM manager allow us to know the state of the SIM |
554 | + that we've got installed. */ |
555 | + private void simmanager_property (string prop, Variant value) |
556 | + { |
557 | + switch (prop) { |
558 | + case "Present": { |
559 | + sim_installed = value.get_boolean(); |
560 | + break; |
561 | + } |
562 | + case "PinRequired": { |
563 | + required_pin = value.get_string(); |
564 | + break; |
565 | + } |
566 | + case "Retries": { |
567 | + if (value.get_type_string() == "a{sy}") { |
568 | + for (int i = 0; i < value.n_children(); i++) { |
569 | + string? key = null; |
570 | + uchar tries = 0; |
571 | + value.get_child(i, "{sy}", &key, &tries); |
572 | + |
573 | + retries[key] = tries; |
574 | + } |
575 | + } |
576 | + break; |
577 | + } |
578 | + } |
579 | + } |
580 | + |
581 | + private void pin_unlock_entered (string pin) |
582 | + { |
583 | + if (required_pin == last_required_pin) { |
584 | + bool retry = false; |
585 | + if (required_pin == "puk") { |
586 | + // if it's a puk, we need to reset the pin. |
587 | + close_all_notifications(true); |
588 | + if (send_unlock(_("Enter new PIN code"), null, 4, pin_reset_new_entered)) { |
589 | + debug("SIM pin request. Sent new pin request"); |
590 | + puk_code = pin; |
591 | + } else { |
592 | + warning("SIM pin request. New pin notification failed"); |
593 | + } |
594 | + } else if (!enter_pin(required_pin, pin, out retry)) { |
595 | + warning("SIM pin request. Failed. retry=$(retry)"); |
596 | + close_all_notifications(true); |
597 | + if (retry) { |
598 | + send_unlock_notification(); |
599 | + } else { |
600 | + send_fail_notification("An unexpected error occurred."); |
601 | + } |
602 | + } else { |
603 | + debug("SIM pin request. Done"); |
604 | + close_all_notifications(true); |
605 | + } |
606 | + } else { |
607 | + warning(@"Required pin type changed. old=$(last_required_pin), new=$(required_pin)"); |
608 | + close_all_notifications(true); |
609 | + } |
610 | + } |
611 | + |
612 | + private void pin_reset_new_entered (string pin) |
613 | + { |
614 | + close_all_notifications(false); |
615 | + |
616 | + if (send_unlock(_("Please confirm PIN code"), null, 4, pin_reset_confirm_entered)) { |
617 | + debug("SIM reset pin request. Sent confirm pin request"); |
618 | + new_pin = pin; |
619 | + } else { |
620 | + warning("SIM reset pin request. Confirm pin notification failed"); |
621 | + clear_stored_data(); |
622 | + } |
623 | + } |
624 | + |
625 | + private void pin_reset_confirm_entered (string pin) |
626 | + { |
627 | + if (new_pin == pin) { |
628 | + if (reset_pin("puk", puk_code, pin)) { |
629 | + debug("SIM reset pin request. Done."); |
630 | + close_all_notifications(true); |
631 | + } else { |
632 | + warning("SIM reset request. Failed."); |
633 | + close_all_notifications(true); |
634 | + send_fail_notification("Failed to reset pin."); |
635 | + } |
636 | + } else { |
637 | + warning("SIM reset request. Pin codes did not match"); |
638 | + close_all_notifications(true); |
639 | + send_fail_notification(_("Pin codes did not match")); |
640 | + } |
641 | + } |
642 | + |
643 | + private void pin_change_old_entered (string pin) |
644 | + { |
645 | + close_all_notifications(true); |
646 | + |
647 | + if (send_unlock(_("Please enter new SIM PIN"), "pin", 4, pin_change_new_entered)) { |
648 | + debug("SIM change pin request. Sent new pin request"); |
649 | + old_pin = pin; |
650 | + } else { |
651 | + warning("SIM change pin request. New pin notification failed"); |
652 | + clear_stored_data(); |
653 | + } |
654 | + } |
655 | + |
656 | + private void pin_change_new_entered (string pin) |
657 | + { |
658 | + close_all_notifications(false); |
659 | + |
660 | + if (send_unlock(_("Please confirm PIN code"), null, 4, pin_change_confirm_entered)) { |
661 | + debug("SIM change pin request. Sent confirm pin request"); |
662 | + new_pin = pin; |
663 | + } else { |
664 | + warning("SIM change pin request. Confirm pin notification failed"); |
665 | + clear_stored_data(); |
666 | + } |
667 | + } |
668 | + |
669 | + private void pin_change_confirm_entered (string pin) |
670 | + { |
671 | + if (new_pin == pin) { |
672 | + if (change_pin("pin", old_pin, new_pin)) { |
673 | + debug("SIM change pin request. Done."); |
674 | + close_all_notifications(true); |
675 | + } else { |
676 | + warning("SIM change pin request. Failed."); |
677 | + close_all_notifications(true); |
678 | + send_fail_notification("Failed to change pin."); |
679 | + } |
680 | + } else { |
681 | + close_all_notifications(true); |
682 | + send_fail_notification(_("Pin codes did not match")); |
683 | + } |
684 | + } |
685 | + |
686 | + private bool enter_pin(string type, string pin, out bool retry) |
687 | + { |
688 | + retry = false; |
689 | + try { |
690 | + if (simmanager != null) { |
691 | + debug(@"SimManager: Entering $(type) pin"); |
692 | + simmanager.enter_pin(type, pin); |
693 | + return true; |
694 | + } |
695 | + } catch (DBusError e) { |
696 | + warning(@"Failed to enter $(type) pin for '$(device.get_iface())': $(e.message)"); |
697 | + } catch (IOError e) { |
698 | + warning(@"Failed to enter $(type) pin for '$(device.get_iface())': $(e.message)"); |
699 | + if (check_ofono_error(e, OFONO_ERROR_FAILED)) { |
700 | + retry = true; |
701 | + } |
702 | + } |
703 | + return false; |
704 | + } |
705 | + |
706 | + private bool reset_pin(string type, string puk, string pin) |
707 | + { |
708 | + try { |
709 | + if (simmanager != null) { |
710 | + debug(@"SimManager: Resetting"); |
711 | + simmanager.reset_pin(type, puk, pin); |
712 | + return true; |
713 | + } |
714 | + } catch (DBusError e) { |
715 | + warning(@"Failed to reset pin for '$(device.get_iface())': $(e.message)"); |
716 | + } catch (IOError e) { |
717 | + warning(@"Failed to reset pin for '$(device.get_iface())': $(e.message)"); |
718 | + } |
719 | + return false; |
720 | + } |
721 | + |
722 | + private bool change_pin(string type, string old_pin, string new_pin) |
723 | + { |
724 | + try { |
725 | + if (simmanager != null) { |
726 | + debug(@"SimManager: Changing $type pin"); |
727 | + simmanager.change_pin(type, old_pin, new_pin); |
728 | + return true; |
729 | + } |
730 | + } catch (DBusError e) { |
731 | + warning(@"Failed to change pin for '$(device.get_iface())': $(e.message)"); |
732 | + } catch (IOError e) { |
733 | + warning(@"Failed to change pin for '$(device.get_iface())': $(e.message)"); |
734 | + } |
735 | + return false; |
736 | + } |
737 | + |
738 | + private void unlock_cancelled() { |
739 | + debug(@"SIM notification cancelled"); |
740 | + close_all_notifications(true); |
741 | + } |
742 | + |
743 | + private void close_all_notifications(bool reset_data) { |
744 | + unowned List<CurrentNotification?>? element = notifications.first (); |
745 | + while (element != null) { |
746 | + CurrentNotification? entry = element.data; |
747 | + if (entry != null) { |
748 | + try { |
749 | + if (entry.unlock_menu_export_id != 0) { |
750 | + conn.unexport_menu_model(entry.unlock_menu_export_id); |
751 | + } |
752 | + if (entry.unlock_actions_export_id != 0) { |
753 | + conn.unexport_action_group(entry.unlock_actions_export_id); |
754 | + } |
755 | + debug(@"closing notification $(entry.notification.id)"); |
756 | + entry.notification.close(); |
757 | + } catch (Error e) { |
758 | + warning("Failed to close notification for '$(device.get_iface())': $(e.message)"); |
759 | + } |
760 | + } |
761 | + element = notifications.next; |
762 | + } |
763 | + if (reset_data) { |
764 | + clear_stored_data(); |
765 | + } |
766 | + } |
767 | + |
768 | + private void clear_stored_data() |
769 | + { |
770 | + new_pin = ""; |
771 | + old_pin = ""; |
772 | + puk_code = ""; |
773 | + } |
774 | + |
775 | + private void clear_notification (CurrentNotification? notification) |
776 | + { |
777 | + if (notification.unlock_menu_export_id != 0) { |
778 | + conn.unexport_menu_model(notification.unlock_menu_export_id); |
779 | + } |
780 | + |
781 | + if (notification.unlock_actions_export_id != 0) { |
782 | + conn.unexport_action_group(notification.unlock_actions_export_id); |
783 | + } |
784 | + } |
785 | + |
786 | + private void notification_closed (Notification? notification) |
787 | + { |
788 | + unowned List<CurrentNotification?>? element = notifications.first (); |
789 | + while (element != null) { |
790 | + unowned CurrentNotification? entry = element.data; |
791 | + |
792 | + if (entry != null && notification.id == entry.notification.id) { |
793 | + debug(@"notification_closed $(notification.id)"); |
794 | + |
795 | + clear_notification(entry); |
796 | + notifications.delete_link(element); |
797 | + break; |
798 | + } |
799 | + element = notifications.next; |
800 | + } |
801 | + if (notifications.length () == 0) { |
802 | + pin_unlocking = false; |
803 | + } |
804 | + } |
805 | + |
806 | + private bool check_ofono_error(Error e, string value) |
807 | + { |
808 | + return DBusError.get_remote_error(e) == value; |
809 | + } |
810 | + |
811 | + } |
812 | +} |
813 | |
814 | === modified file 'network/network-action-manager.vala' |
815 | --- network/network-action-manager.vala 2013-10-09 20:27:33 +0000 |
816 | +++ network/network-action-manager.vala 2013-10-10 15:57:20 +0000 |
817 | @@ -112,7 +112,7 @@ |
818 | /* Check to see if the modem supports voice */ |
819 | try { |
820 | oFono.Modem? ofono_modem = watched_modems.lookup(modemmaybe.get_iface()); |
821 | - |
822 | + |
823 | if (ofono_modem == null) { |
824 | ofono_modem = Bus.get_proxy_sync (BusType.SYSTEM, "org.ofono", modemmaybe.get_iface()); |
825 | |
826 | @@ -133,15 +133,15 @@ |
827 | return; |
828 | } |
829 | |
830 | - if (!variant_contains(interfaces, "org.ofono.VoiceCallManager")) { |
831 | + if (!Utils.variant_contains(interfaces, "org.ofono.VoiceCallManager")) { |
832 | debug(@"Modem '$(modemmaybe.get_iface())' doesn't have voice support only: $(interfaces.print(false))"); |
833 | return; |
834 | } |
835 | - if (!variant_contains(interfaces, "org.ofono.SimManager")) { |
836 | + if (!Utils.variant_contains(interfaces, "org.ofono.SimManager")) { |
837 | debug(@"Modem '$(modemmaybe.get_iface())' doesn't have SIM management support only: $(interfaces.print(false))"); |
838 | return; |
839 | } |
840 | - if (!variant_contains(interfaces, "org.ofono.NetworkRegistration")) { |
841 | + if (!Utils.variant_contains(interfaces, "org.ofono.NetworkRegistration")) { |
842 | debug(@"Modem '$(modemmaybe.get_iface())' doesn't have Network Registration support only: $(interfaces.print(false))"); |
843 | return; |
844 | } |
845 | @@ -304,24 +304,6 @@ |
846 | return "pre-edge"; |
847 | } |
848 | |
849 | - private bool variant_contains (Variant variant, string needle) |
850 | - { |
851 | - if (variant.is_of_type(VariantType.VARIANT)) |
852 | - return variant_contains(variant.get_variant(), needle); |
853 | - |
854 | - if (!variant.is_container()) |
855 | - return false; |
856 | - |
857 | - Variant item; |
858 | - var iter = new VariantIter(variant); |
859 | - for (item = iter.next_value(); item != null; item = iter.next_value()) { |
860 | - if (item.get_string() == needle) |
861 | - return true; |
862 | - } |
863 | - |
864 | - return false; |
865 | - } |
866 | - |
867 | private Variant? icon_serialize (string icon_name) |
868 | { |
869 | try { |
870 | @@ -557,13 +539,13 @@ |
871 | var dev = act_dev as NM.DeviceWifi; |
872 | |
873 | dev.notify["active-access-point"].disconnect (active_access_point_changed); |
874 | - |
875 | + |
876 | if (act_ap != null) |
877 | { |
878 | act_ap.notify["strength"].disconnect (active_connection_strength_changed); |
879 | act_ap = null; |
880 | } |
881 | - |
882 | + |
883 | break; |
884 | default: |
885 | break; |
886 | |
887 | === modified file 'network/network-menu-service.vala' |
888 | --- network/network-menu-service.vala 2013-08-25 18:53:54 +0000 |
889 | +++ network/network-menu-service.vala 2013-10-10 15:57:20 +0000 |
890 | @@ -19,6 +19,7 @@ |
891 | */ |
892 | |
893 | using Config; |
894 | +using Notify; |
895 | |
896 | public static int main (string[] args) |
897 | { |
898 | @@ -32,10 +33,10 @@ |
899 | |
900 | GLib.Unix.signal_add(GLib.ProcessSignal.TERM, () => { |
901 | debug("Recieved SIGTERM"); |
902 | - mainloop.quit(); |
903 | + mainloop.quit(); |
904 | return false; |
905 | }); |
906 | - |
907 | + Notify.init("indicator-network"); |
908 | var menu = new Network.NetworkMenu (); |
909 | mainloop.run (); |
910 | return 0; |
911 | |
912 | === modified file 'network/network-menu.vala' |
913 | --- network/network-menu.vala 2013-09-30 18:48:11 +0000 |
914 | +++ network/network-menu.vala 2013-10-10 15:57:20 +0000 |
915 | @@ -179,6 +179,7 @@ |
916 | private NM.Client client; |
917 | private ActionManager am; |
918 | private GLibLocal.ActionMuxer muxer = new GLibLocal.ActionMuxer(); |
919 | + private GLib.DBusConnection conn; |
920 | |
921 | public NetworkMenu () |
922 | { |
923 | @@ -190,7 +191,7 @@ |
924 | |
925 | try |
926 | { |
927 | - var conn = Bus.get_sync (BusType.SESSION, null); |
928 | + conn = Bus.get_sync (BusType.SESSION, null); |
929 | |
930 | conn.export_action_group (ACTION_GROUP_PATH, muxer as ActionGroup); |
931 | |
932 | @@ -236,7 +237,7 @@ |
933 | private void add_device (NM.Device device) |
934 | { |
935 | Device.Base? founddev = null; |
936 | - |
937 | + |
938 | founddev = desktop.find_device(device.get_path()); |
939 | if (founddev != null) return; |
940 | |
941 | @@ -256,9 +257,9 @@ |
942 | |
943 | break; |
944 | case NM.DeviceType.MODEM: |
945 | - var mobiledesktopdev = new Device.Mobile(this.client, device as NM.DeviceModem, this.muxer, true); |
946 | + var mobiledesktopdev = new Device.Mobile(this.client, device as NM.DeviceModem, this.muxer, true, conn); |
947 | desktop.append_device(mobiledesktopdev); |
948 | - var mobilephonedev = new Device.Mobile(this.client, device as NM.DeviceModem, this.muxer, false); |
949 | + var mobilephonedev = new Device.Mobile(this.client, device as NM.DeviceModem, this.muxer, false, conn); |
950 | phone.append_device(mobilephonedev); |
951 | break; |
952 | case NM.DeviceType.ETHERNET: |
953 | |
954 | === modified file 'network/ofono.vala' |
955 | --- network/ofono.vala 2013-08-22 20:57:12 +0000 |
956 | +++ network/ofono.vala 2013-10-10 15:57:20 +0000 |
957 | @@ -5,23 +5,53 @@ |
958 | |
959 | [DBus (name = "org.ofono.Modem") ] |
960 | public interface Modem : GLib.Object { |
961 | + |
962 | + [DBus (name = "SetProperty")] |
963 | public abstract void set_property (string property, GLib.Variant value) throws IOError; |
964 | + |
965 | + [DBus (name = "GetProperties")] |
966 | public abstract GLib.HashTable<string, GLib.Variant> get_properties () throws IOError; |
967 | + |
968 | + [DBus (name = "PropertyChanged")] |
969 | public signal void property_changed (string property, GLib.Variant value); |
970 | } |
971 | |
972 | [DBus (name = "org.ofono.NetworkRegistration") ] |
973 | public interface NetworkRegistration : GLib.Object { |
974 | + |
975 | + [DBus (name = "GetProperties")] |
976 | public abstract GLib.HashTable<string, GLib.Variant> get_properties () throws IOError; |
977 | |
978 | + [DBus (name = "PropertyChanged")] |
979 | public signal void property_changed (string property, GLib.Variant value); |
980 | } |
981 | |
982 | [DBus (name = "org.ofono.SimManager") ] |
983 | public interface SIMManager : GLib.Object { |
984 | + |
985 | + [DBus (name = "SetProperty")] |
986 | public abstract void set_property (string property, GLib.Variant value) throws IOError; |
987 | + |
988 | + [DBus (name = "GetProperties")] |
989 | public abstract GLib.HashTable<string, GLib.Variant> get_properties () throws IOError; |
990 | + |
991 | + [DBus (name = "PropertyChanged")] |
992 | public signal void property_changed (string property, GLib.Variant value); |
993 | + |
994 | + [DBus (name = "ChangePin")] |
995 | + public abstract void change_pin(string type, string old_pin, string new_pin) throws DBusError, IOError; |
996 | + |
997 | + [DBus (name = "EnterPin")] |
998 | + public abstract void enter_pin(string type, string pin) throws DBusError, IOError; |
999 | + |
1000 | + [DBus (name = "ResetPin")] |
1001 | + public abstract void reset_pin(string type, string puk, string new_pin) throws DBusError, IOError; |
1002 | + |
1003 | + [DBus (name = "LockPin")] |
1004 | + public abstract void lock_pin(string type, string pin) throws DBusError, IOError; |
1005 | + |
1006 | + [DBus (name = "UnlockPin")] |
1007 | + public abstract void unlock_pin(string type, string pin) throws DBusError, IOError; |
1008 | } |
1009 | |
1010 | |
1011 | |
1012 | === added file 'network/utils.vala' |
1013 | --- network/utils.vala 1970-01-01 00:00:00 +0000 |
1014 | +++ network/utils.vala 2013-10-10 15:57:20 +0000 |
1015 | @@ -0,0 +1,23 @@ |
1016 | +using GLib; |
1017 | + |
1018 | +namespace Utils { |
1019 | + |
1020 | + public bool variant_contains (Variant variant, string needle) |
1021 | + { |
1022 | + if (variant.is_of_type(VariantType.VARIANT)) |
1023 | + return variant_contains(variant.get_variant(), needle); |
1024 | + |
1025 | + if (!variant.is_container()) |
1026 | + return false; |
1027 | + |
1028 | + Variant item; |
1029 | + var iter = new VariantIter(variant); |
1030 | + for (item = iter.next_value(); item != null; item = iter.next_value()) { |
1031 | + if (item.get_string() == needle) |
1032 | + return true; |
1033 | + } |
1034 | + |
1035 | + return false; |
1036 | + } |
1037 | + |
1038 | +} |
I would really like to see some tests for this along the lines of what I did for the secret agent (https:/ /code.launchpad .net/~indicator -applet- developers/ indicator- network/ secret- agent/+ merge/182898).