Updated connman and bluetooth settings to the new connman protocols in settings-daemon
[profile/ivi/SettingsApp.git] / js / panel-connman.js
1 /*
2  * Copyright (c) 2013, Intel Corporation.
3  *
4  * This program is licensed under the terms and conditions of the
5  * Apache License, version 2.0.  The full text of the Apache License is at
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  */
9 var connmanScanInProgress = false;
10
11 function connmanPanelInit() {
12
13     /* Connman Settings Panel */
14     $('#page_connman').on('pageshow', function(event, data) {
15         if (data.prevPage.attr('id') === 'page_connman_service') return;
16
17         wsAPI.subscribeEvents(connmanEventReceived);
18
19         console.log('Get all the available technologies');
20         $('#connman_technologies').html('');
21         settings.connman.getTechnologies(function(technologies) {
22             for (var i = 0; i < technologies.length; i++) {
23                 var technology = technologies[i];
24                 console.log('Connman technology found: ' + technology.prop.Type);
25                 if (technology.prop.Type === 'bluetooth') {
26                     /* Do not show bluetooth */
27                     continue;
28                 }
29                 if (technology.prop.Powered === undefined) {
30                     (function(t) {
31                         t.getPowered(function(is_powered) {
32                             t.prop.Powered = is_powered;
33                             connmanConstructTechnologyElement(t);
34                         }, function(e) {
35                             t.prop.Powered = false;
36                             connmanConstructTechnologyElement(t);
37                         });
38                     })(technology);
39                 } else {
40                     connmanConstructTechnologyElement(technology);
41                 }
42             }
43         }, function(e) {
44             showMsg('Error', e);
45         });
46     });
47
48     $('#button_connman_refresh').on('click', function() {
49         /* connman only supports wifi scan */
50         if ($('#toggle_connman_wifi').val() === 'on') {
51             connmanScan('/net/connman/technology/wifi');
52         } else {
53             connmanSync(null, function(e) {
54                 showMsg('Error', e);
55             });
56         }
57     });
58
59     $('#button_connman_wifi_add').on('click', function() {
60         if (connmanScanInProgress) return;
61
62         console.log('Enter add WiFi page');
63         $.mobile.changePage('#page_wifi_add');
64     });
65
66     /* Connman service detail page */
67     $('#page_connman_service').on('pageshow', function(event, data) {
68         var service_id = localStorage.getItem('connman_service_id');
69         if (service_id == undefined) return;
70         var service = $(jqId(service_id)).data('service-object');
71         connmanConstructServicePanel(service);
72     });
73
74     /* WiFi add new network page */
75     $('#page_wifi_add').on('pagebeforeshow', function(event, data) {
76         $('#input_wifi_add_ssid').val('');
77         $('#input_wifi_add_password').val('');
78         $('#radio_wifi_none').prop('checked', true);
79         $('input[name=radio_wifi_security]').checkboxradio('refresh');
80         $('#input_wifi_add_password').textinput('disable');
81     });
82
83     $('#button_wifi_add').on('click', function() {
84         var name = $('#input_wifi_add_ssid').val();
85         var security = $('input[name=radio_wifi_security]:checked').val();
86         var passcode = $('#input_wifi_add_password').val();
87
88         if (name === '') {
89             showMsg('Error', 'Enter SSID');
90             return;
91         }
92
93         if (security !== 'none' && passcode === '') {
94             showMsg('Error', 'Enter passphrase');
95             return;
96         }
97
98         showSpinner(false, 'Adding...');
99         connmanGetHiddenServicePath(security, function(service_path) {
100             var service = new settings.connman.ConnmanService('', {
101                 'Name': name,
102                 'EncryptionMode': security
103             });
104             service.connect(passcode, function() {
105                 /* success */
106                 connmanSync(function() {
107                     hideSpinner();
108                     console.log('Go back to Connman page');
109                     $.mobile.changePage('#page_connman');
110                 }, function(e) {
111                     showMsg('Error', e);
112                 });
113             }, function(e) {
114                 /* error */
115                 hideSpinner();
116                 showMsg('Error', 'Failed to add network: ' + e);
117             });
118         }, function(e) {
119             hideSpinner();
120             showMsg('Error', e);
121         });
122     });
123
124     $('[name="radio_wifi_security"]').on('change', function(event, data) {
125         var security = $('input[name=radio_wifi_security]:checked').val();
126
127         if (security === 'none') {
128             $('#input_wifi_add_password').textinput('disable');
129             $('#input_wifi_add_password').val('');
130         } else {
131             $('#input_wifi_add_password').textinput('enable');
132         }
133     });
134
135     $('#button_wifi_connect').on('click', function() {
136         var service_id = localStorage.getItem('connman_service_id');
137         if (service_id == undefined) return;
138         var service = $(jqId(service_id)).data('service-object');
139         var passcode = $('#input_wifi_connect_passphrase').val();
140         connmanConnectToService(service, passcode, function() {
141             console.log('Successfully connected to service: ' + service.prop.Name);
142             if ($.mobile.activePage.attr('id') !== 'page_connman') {
143                 console.log('Go back to Connman page');
144                 $.mobile.changePage('#page_connman');
145             }
146         }, function(e) {
147             showMsg('Error', 'Connect service failed: ' + e);
148         });
149     });
150 }
151
152 function connmanEventReceived(event) {
153     if (event.type === WS_EVENT_TYPE.CONNMAN) {
154         if ($.mobile.activePage.attr('id') !== 'page_connman' && $.mobile.activePage.attr('id') !== 'page_connman_service') {
155             return;
156         }
157
158         if (event.name === 'ServicesChanged') {
159             connmanHandleServicesChanged(event.id, event.value);
160         } else if (event.name === 'PropertyChanged') {
161             connmanHandlePropertyChanged(event.id, event.value);
162         } else {
163             console.log('Unsupported event received: ' + event.name);
164         }
165     }
166 }
167
168 function connmanHandleServicesChanged(object_path, services) {
169     var servicesChanged = services[0];
170     var servicesRemoved = services[1];
171
172     console.log(servicesChanged.length + ' services changed');
173     for (var i = 0; i < servicesChanged.length; i++) {
174         if (servicesChanged[i][0] === undefined) {
175             console.log('Invalid parameters, missing object path');
176             continue;
177         }
178
179         var service = $(jqId(servicesChanged[i][0])).data('service-object');
180
181         if (service == null) {
182             console.log('could not find service object ' + servicesChanged[i][0]);
183             continue;
184         }
185
186         for (var prop in servicesChanged[i][1]) {
187             if (servicesChanged[i][1].hasOwnProperty(prop)) {
188                 service.prop.prop = servicesChanged[i][1].prop;
189                 connmanUpdateService(service);
190                 console.log('Service ' + service.prop.Name + ' updated: ' + prop);
191             }
192         }
193     }
194
195     console.log(servicesRemoved.length + ' services removed');
196     for (var i = 0; i < servicesRemoved.length; i++) {
197         if (servicesRemoved[i] === undefined) {
198             console.log('Invalid parameters, missing object path');
199             continue;
200         }
201
202         connmanRemoveFromAvailableList(servicesRemoved[i]);
203     }
204 }
205
206 function connmanHandlePropertyChanged(id, property) {
207     if (property[0] === 'Powered') {
208         var index = id.lastIndexOf('/');
209         var technology = id.substring(index + 1);
210         if (property[1] === true) {
211             connmanToggleOn(technology);
212             if (technology === 'wifi') {
213                 setTimeout(function() {
214                     /* add a 1 sec delay */
215                     connmanScan(id);
216                 }, 1000);
217             }
218         } else {
219             connmanToggleOff(technology);
220         }
221     }
222
223     if (property[0] === 'State') {
224         /* specific service has changed */
225         var service = $(jqId(id)).data('service-object');
226         if (service == null) {
227             console.error('Connman service not found ' + id);
228             return;
229         }
230
231         service.prop.State = property[1];
232         connmanUpdateService(service);
233     } else if (property[0] === 'Connected') {
234         /* unknown service has changed, sync Connman */
235         console.log('Unknown service connected property changed');
236         connmanSync(null, function(e) {
237             showMsg('Error', e);
238         });
239     }
240 }
241
242 function connmanClearAvailableList() {
243     $('#listview_services_available').html('');
244 }
245
246 function connmanScan(technology) {
247     if (technology !== '/net/connman/technology/wifi') {
248         /* Connman only supports wifi scan */
249         return;
250     }
251
252     if (connmanScanInProgress) {
253         console.log('Connman scan in progress...');
254         return;
255     }
256
257     console.log('Start connman scan');
258     connmanScanInProgress = true;
259
260     /* clear the services list with new scan */
261     connmanClearAvailableList();
262
263     showSpinner(false, 'Scanning...');
264     $('#toggle_connman_wifi').slider('disable');
265     $('#toggle_connman_wifi').slider('refresh');
266     settings.connman.scan(technology, function(services) {
267         hideSpinner();
268         connmanScanInProgress = false;
269         $('#toggle_connman_wifi').slider('enable');
270         $('#toggle_connman_wifi').slider('refresh');
271         console.log('found ' + services.length + ' connman services');
272         for (var i = 0; i < services.length; i++) {
273             var service = services[i];
274             if (service.prop.Type === undefined || service.prop.Type === 'bluetooth') {
275                 console.log('Ignore bluetooth or unknown services');
276                 continue;
277             }
278             if (service.prop.Name === undefined) {
279                 console.log('Ignore hidden service - ' + service.id);
280                 continue;
281             }
282
283             connmanUpdateService(service);
284         }
285     }, function(e) {
286         hideSpinner();
287         connmanScanInProgress = false;
288         $('#toggle_connman_wifi').slider('enable');
289         $('#toggle_connman_wifi').slider('refresh');
290         showMsg('Error', 'Cannot scan: ' + e);
291     });
292 }
293
294 function connmanSync(success_cb, error_cb) {
295     if (connmanScanInProgress) {
296         console.log('Connman scan in progress...');
297         return;
298     }
299
300     console.log('Start connman sync');
301     /* clear the services list with new scan */
302     connmanClearAvailableList();
303
304     wifiScanInProgress = true;
305     $('#toggle_connman_wifi').slider('disable');
306     $('#toggle_connman_wifi').slider('refresh');
307     settings.connman.getServices(function(services) {
308         connmanScanInProgress = false;
309         $('#toggle_connman_wifi').slider('enable');
310         $('#toggle_connman_wifi').slider('refresh');
311         console.log('found ' + services.length + ' connman services');
312         for (var i = 0; i < services.length; i++) {
313             var service = services[i];
314             if (service.prop.Type === undefined || service.prop.Type === 'bluetooth') {
315                 console.log('Ignore bluetooth or unknown services');
316                 continue;
317             }
318             if (service.prop.Name === undefined) {
319                 console.log('Ignore hidden service - ' + service.id);
320                 continue;
321             }
322
323             connmanUpdateService(service);
324         }
325         if (success_cb) {
326             success_cb();
327         }
328     }, function(e) {
329         connmanScanInProgress = false;
330         $('#toggle_connman_wifi').slider('enable');
331         $('#toggle_connman_wifi').slider('refresh');
332         if (error_cb) {
333             error_cb(e);
334         }
335     });
336 }
337
338 function connmanRefreshServicesList() {
339     $('#listview_services_available').listview('refresh');
340 }
341
342 function connmanAppendToAvailableList(service) {
343     if ($('#listview_services_available').find(jqId(service.id)).length != 0) return;
344
345     var parent = '#listview_services_available';
346     connmanConstructServiceElement(parent, service);
347     connmanUpdateServiceButton(service);
348     connmanRefreshServicesList();
349 }
350
351 function connmanRemoveFromAvailableList(service_id) {
352     var removeThis = $('#listview_services_available li').filter(function() {
353         return $(this).find(jqId(service_id)).length === 1;
354     });
355
356     if (removeThis.length !== 0) {
357         removeThis.remove();
358         connmanRefreshServicesList();
359     }
360 }
361
362 function getSignalStrengthStr(strength) {
363     var signal_strength = 'unknown';
364     if (strength > 0 && strength <= 20) {
365         strength = 'very poor';
366     } else if (strength > 20 && strength <= 40) {
367         signal_strength = 'poor';
368     } else if (strength > 40 && strength <= 70) {
369         signal_strength = 'average';
370     } else if (strength > 70 && strength <= 90) {
371         signal_strength = 'good';
372     } else if (strength > 90 && strength <= 100) {
373         signal_strength = 'excellent';
374     }
375     return signal_strength;
376 }
377
378 function connmanConstructTechnologyElement(technology) {
379     if (technology.prop.Type === undefined || technology.prop.Name == undefined) {
380         console.error('technology type or name missing ' + technology);
381         return;
382     }
383
384     var html = '<ul data-role="listview" data-inset="true" class="ui-listview ui-listview-inset">';
385     html += '<li data-role="fieldcontain">';
386     html += '<label for="toggle_connman_' + technology.prop.Type + '" class="ui-slider">' + technology.prop.Name + '</label>';
387     html += '<select data-role="slider" name="toggle_connman_' + technology.prop.Type + '" ';
388     html += 'id="toggle_connman_' + technology.prop.Type + '" class="ui-slider-switch">';
389     html += '<option value="off">Off</option>';
390     html += '<option value="on">On</option>';
391     html += '</select></li></ul>';
392     $('#connman_technologies').append(html).trigger('create');
393
394     console.log('Connman technology ' + technology.prop.Type + ' is powered: ' + technology.prop.Powered);
395     if (technology.prop.Powered) {
396         connmanToggleOn(technology.prop.Type);
397         if ($('ul#listview_services_available li').length === 0) {
398             /* connman only supports wifi scan */
399             if (technology.prop.Type === 'wifi') {
400                 connmanScan(technology.id);
401             } else {
402                 connmanSync(null, function(e) {
403                     showMsg('Error', e);
404                 });
405             }
406         }
407     } else {
408         connmanToggleOff(technology.prop.Type);
409     }
410
411     $('#toggle_connman_' + technology.prop.Type).change(function() {
412         console.log('toggle ' + technology.prop.Type + ' changed');
413
414         if (connmanScanInProgress) {
415             console.log('Connman scan in progress...');
416             return;
417         }
418
419         $('#toggle_connman_' + technology.prop.Type).slider('disable');
420         $('#toggle_connman_' + technology.prop.Type).slider('refresh');
421         if ($('#toggle_connman_' + technology.prop.Type).val() === 'off') {
422             technology.setPowered(false, function() {
423                 /* success */
424                 $('#toggle_connman_' + technology.prop.Type).slider('enable');
425                 $('#toggle_connman_' + technology.prop.Type).slider('refresh');
426                 console.log('Successfully disabled connman technology ' + technology.prop.Type);
427             }, function(e) {
428                 /* error */
429                 hideSpinner();
430                 $('#toggle_connman_' + technology.prop.Type).slider('enable');
431                 $('#toggle_connman_' + technology.prop.Type).val('on').slider('refresh');
432                 showMsg('Error', 'Cannot disable ' + technology.prop.Type);
433             });
434         } else {
435             technology.setPowered(true, function() {
436                 /* success */
437                 $('#toggle_connman_' + technology.prop.Type).slider('enable');
438                 $('#toggle_connman_' + technology.prop.Type).slider('refresh');
439                 if (technology.prop.Type === 'wifi') {
440                     setTimeout(function() {
441                         /* add a 1 sec delay */
442                         connmanScan(technology.id);
443                     }, 1000);
444                 }
445                 console.log('Successfully enabled connman technology ' + technology.prop.Type);
446             }, function(e) {
447                 /* error */
448                 hideSpinner();
449                 $('#toggle_connman_' + technology.prop.Type).slider('enable');
450                 $('#toggle_connman_' + technology.prop.Type).val('off').slider('refresh');
451                 showMsg('Error', 'Cannot enable ' + technology.prop.Type);
452             });
453         }
454     });
455 }
456
457 function connmanConstructServiceElement(parent, service) {
458     if (service.id === undefined || service.prop.Type === undefined) {
459         console.error('service id or type missing ' + service);
460         return;
461     }
462
463     var html = '<li data-icon="false"><a href="#" id="' + jqId(service.id).replace('#', '') + '">';
464     html += '<div class="service-ssid">' + service.prop.Name + '</div>';
465
466     if (service.prop.Type === 'wifi') {
467         html += '<div class="service-encryption">Encryption: ' + service.prop.EncryptionMode + '</div>';
468         html += '<div class="service-strength">Signal: ' + getSignalStrengthStr(service.prop.Strength) + '</div>';
469     }
470     html += '<div class="service-status"></div>';
471     html += '<div data-role="button" class="service-action-button ui-li-aside" data-inline="true"></div>';
472     html += '</a></li>';
473     $(parent).append(html).trigger('create');
474
475     /* store service object in the element so we can reference it later */
476     $(jqId(service.id)).data('service-object', service);
477
478     $(jqId(service.id)).on('click', function() {
479         /* BUG in webruntime that cause click event when another page is rendered */
480         if ($.mobile.activePage.attr('id') === 'page_connman') {
481             localStorage.setItem('connman_service_id', service.id);
482             console.log('Enter Connman service page');
483             $.mobile.changePage('#page_connman_service');
484         }
485     });
486
487     $(jqId(service.id)).find('div.service-action-button').on('click', function(e) {
488         var parent = $(this).parent().attr('id');
489
490         /*
491          * prevent the click event to propagate up
492          */
493         e.stopImmediatePropagation();
494         e.preventDefault();
495
496         /* retrieve the service object from element */
497         var service = $(jqId(parent)).data('service-object');
498         if (service == null) {
499             console.error('Connman service object not found');
500             return;
501         }
502
503         if (service.prop.State === 'idle') {
504             if (service.prop.EncryptionMode === 'none') {
505                 connmanConnectToService(service, null);
506             } else {
507                 connmanConnectToService(service, null, null, function(e) {
508                     console.log('Connect failed, will ask for passphrase');
509                     localStorage.setItem('connman_service_id', service.id);
510                     console.log('Enter connect WiFi page');
511                     $.mobile.changePage('#page_wifi_connect');
512                 });
513             }
514         } else if (service.prop.State === 'ready') {
515             console.log('Disconnecting from service: ' + service.prop.Name);
516             showSpinner(false, 'Disconnecting...');
517             service.disconnect(function() {
518                 /* success */
519                 connmanSync(function() {
520                     hideSpinner();
521                 }, function(e) {
522                     showMsg('Error', e);
523                 })
524             }, function(e) {
525                 /* error */
526                 hideSpinner();
527                 showMsg('Error', 'Disconnect service failed: ' + e);
528             });
529         }
530     });
531 }
532
533 function connmanUpdateService(service) {
534     connmanAppendToAvailableList(service);
535
536     /* update service button for allowed action */
537     connmanUpdateServiceButton(service);
538
539     /* update service connection status */
540     connmanUpdateConnectionStatus(service);
541
542     /* update service detail panel */
543     if ($.mobile.activePage.attr('id') === 'page_connman_service') {
544         var service_id = localStorage.getItem('connman_service_id');
545         if (service_id == undefined) return;
546         var service_object = $(jqId(service_id)).data('service-object');
547         if (service.id === service_object.id) {
548             connmanConstructServicePanel(service);
549         }
550     }
551 }
552
553 function connmanUpdateServiceButton(service) {
554     if (service.prop.State === 'ready') {
555         $(jqId(service.id)).find('div.service-action-button').find('span').text('Disconnect');
556     } else if (service.prop.State === 'idle' || service.prop.State === 'online') {
557         $(jqId(service.id)).find('div.service-action-button').find('span').text('Connect');
558     }
559 }
560
561 function connmanUpdateConnectionStatus(service) {
562     var status = 'disconnected';
563     if (service.prop.State === 'ready') {
564         $(jqId(service.id)).addClass('service-connected');
565         status = 'connected';
566     } else if (service.prop.State === 'idle' || service.prop.State === 'online') {
567         status = 'disconnected';
568         $(jqId(service.id)).removeClass('service-connected');
569     }
570
571     connmanUpdateConnectionStatusText(service, status);
572 }
573
574 function connmanUpdateConnectionStatusText(service, status) {
575     $(jqId(service.id)).find('div.service-status').text(status);
576 }
577
578 function connmanConstructServicePanel(service) {
579     var status_connected = 'No';
580
581     if (service == null) return;
582     if (service.prop.State === 'ready') status_connected = 'Yes';
583
584     $('#page_connman_service_content').html('');
585     var html = '<ul data-role="listview" id="listview_connman_service" data-inset="true" ' + 'class="service-list ui-listview">';
586     html += '<li id="connman_service_name"><h2>Name: ' + service.prop.Name + '</h2></li>';
587     html += '<li id="connman_service_type"><h2>Type: ' + service.prop.Type + '</h2></li>';
588     html += '<li id="connman_service_type"><h2>State: ' + service.prop.State + '</h2></li>';
589     if (service.prop.Type === 'ethernet' || service.prop.Type === 'wifi') {
590         if (service.prop.Type === 'wifi') {
591             html += '<li id="connman_service_bssid"><h2>SSID: ' + service.prop.BSSID + '</h2></li>';
592             html += '<li id="connman_service_encryption"><h2>Encryption: ' + service.prop.EncryptionMode + '</h2></li>';
593             html += '<li id="connman_service_strength"><h2>Signal Strength: ' + service.prop.Strength + '</h2></li>';
594         }
595         if (service.prop.State === 'ready') {
596             html += '<li id="connman_service_ip_address"><h2>IP Address: ' + service.prop.IPv4.Address + '</h2></li>';
597             html += '<li id="connman_servicel_gateway"><h2>Gateway: ' + service.prop.IPv4.Gateway + '</h2></li>';
598             html += '<li id="connman_service_netmask"><h2>Netmask: ' + service.prop.IPv4.Netmask + '</h2></li>';
599         }
600     }
601     html += '<li id="connman_service_connected"><h2>Connected: ' + status_connected + '</h2></li>';
602     html += '<li id="connman_service_autoconnect"><h2>AutoConnect: ' + service.prop.AutoConnect + '</h2></li>';
603     html += '</ul>';
604     $('#page_connman_service_content').append(html).trigger('create');
605     $('#listview_connman_services').listview('refresh');
606 }
607
608 function connmanUpdateServicePanel(service) {
609     var status_connected = 'No';
610
611     if (service == null) return;
612     if (service.prop.State === 'ready') status_connected = 'Yes';
613     $('#connman_service_connected').text(status_connected);
614     $('#listview_connman_service').listview('refresh');
615 }
616
617 function connmanToggleOn(technology_type) {
618     setTimeout(function() {
619         $('#toggle_connman_' + technology_type).val('on').slider('refresh');
620         console.log('Turn on toggle #toggle_connman_' + technology_type);
621     }, 1000);
622 }
623
624 function connmanToggleOff(technology_type) {
625     setTimeout(function() {
626         $('#toggle_connman_' + technology_type).val('off').slider('refresh');
627         console.log('Turn off toggle #toggle_connman_' + technology_type);
628     }, 1000);
629 }
630
631 function connmanConnectToService(service, passphrase, success_cb, error_cb) {
632     console.log('Connect to service: ' + service.prop.Name);
633     showSpinner(false, 'Connecting...');
634     service.connect(passphrase, function() {
635         /* success */
636         connmanSync(function() {
637             hideSpinner();
638             if (success_cb) {
639                 success_cb();
640             }
641         }, function(e) {
642             hideSpinner();
643             if (error_cb) {
644                 error_cb(e);
645             }
646         });
647     }, function(e) {
648         /* error */
649         hideSpinner();
650         if (error_cb) {
651             error_cb(e);
652         }
653     });
654 }
655
656 function connmanGetHiddenServicePath(security_type, success_cb, error_cb) {
657     if (security_type === undefined || success_cb === undefined || error_cb === undefined) return;
658     settings.connman.scan(function(services) {
659         connmanScanInProgress = false;
660         console.log('found ' + services.length + ' connman services');
661         for (var i = 0; i < services.length; i++) {
662             var service = services[i];
663             if (service.prop.Name === undefined && service.prop.Type === 'wifi') {
664                 console.log('Hidden network matched - ' + service.id);
665                 success_cb(service.id);
666                 return;
667             }
668         }
669         error_cb('No hidden network with security: ' + security_type);
670     }, function(e) {
671         connmanScanInProgress = false;
672         if (error_cb) {
673             error_cb(e);
674         }
675     });
676 }